/*****************************************************************************
 *                                McPAT/CACTI
 *                      SOFTWARE LICENSE AGREEMENT
 *            Copyright 2012 Hewlett-Packard Development Company, L.P.
 *                          All Rights Reserved
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are
 * met: redistributions of source code must retain the above copyright
 * notice, this list of conditions and the following disclaimer;
 * redistributions in binary form must reproduce the above copyright
 * notice, this list of conditions and the following disclaimer in the
 * documentation and/or other materials provided with the distribution;
 * neither the name of the copyright holders nor the names of its
 * contributors may be used to endorse or promote products derived from
 * this software without specific prior written permission.

 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.”
 *
 ***************************************************************************/

#include <string.h>
#include <fstream>
#include <iostream>
#include <sstream>

#include "Ucache.h"
#include "arbiter.h"
#include "area.h"
#include "basic_circuit.h"
#include "crossbar.h"
#include "io.h"
#include "nuca.h"
#include "parameter.h"
#include "version_cacti.h"
//#include "highradix.h"

using namespace std;

namespace cacti {

InputParameter::InputParameter()
    : array_power_gated(false),
      bitline_floating(false),
      wl_power_gated(false),
      cl_power_gated(false),
      interconect_power_gated(false),
      power_gating(false),
      perfloss(0.01),
      cl_vertical(true),
      long_channel_device(false) {
  dvs_voltage = std::vector<double>(0);
}

/* Parses "cache.cfg" file */
void InputParameter::parse_cfg(const string &in_file) {
  FILE *fp = fopen(in_file.c_str(), "r");
  char line[5000];
  char jk[5000];
  char temp_var[5000];
  double temp_double;
  char *data = line;
  int offset = 0;

  if (!fp) {
    cout << in_file << " is missing!\n";
    exit(-1);
  }

  while (fscanf(fp, "%[^\n]\n", line) != EOF) {
    if (!strncmp("-size", line, strlen("-size"))) {
      sscanf(line, "-size %[(:-~)*]%u", jk, &(cache_sz));
      continue;
    }

    if (!strncmp("-page size", line, strlen("-page size"))) {
      sscanf(line, "-page size %[(:-~)*]%u", jk, &(page_sz_bits));
      continue;
    }

    if (!strncmp("-burst length", line, strlen("-burst length"))) {
      sscanf(line, "-burst %[(:-~)*]%u", jk, &(burst_len));
      continue;
    }

    if (!strncmp("-internal prefetch width", line,
                 strlen("-internal prefetch width"))) {
      sscanf(line, "-internal prefetch %[(:-~)*]%u", jk, &(int_prefetch_w));
      continue;
    }

    if (!strncmp("-block", line, strlen("-block"))) {
      sscanf(line, "-block size (bytes) %d", &(line_sz));
      continue;
    }

    if (!strncmp("-associativity", line, strlen("-associativity"))) {
      sscanf(line, "-associativity %d", &(assoc));
      continue;
    }

    if (!strncmp("-read-write", line, strlen("-read-write"))) {
      sscanf(line, "-read-write port %d", &(num_rw_ports));
      continue;
    }

    if (!strncmp("-exclusive read", line, strlen("exclusive read"))) {
      sscanf(line, "-exclusive read port %d", &(num_rd_ports));
      continue;
    }

    if (!strncmp("-exclusive write", line, strlen("-exclusive write"))) {
      sscanf(line, "-exclusive write port %d", &(num_wr_ports));
      continue;
    }

    if (!strncmp("-single ended", line, strlen("-single ended"))) {
      sscanf(line, "-single %[(:-~)*]%d", jk, &(num_se_rd_ports));
      continue;
    }

    if (!strncmp("-search", line, strlen("-search"))) {
      sscanf(line, "-search port %d", &(num_search_ports));
      continue;
    }

    if (!strncmp("-UCA bank", line, strlen("-UCA bank"))) {
      sscanf(line, "-UCA bank%[((:-~)| )*]%d", jk, &(nbanks));
      continue;
    }

    if (!strncmp("-technology", line, strlen("-technology"))) {
      sscanf(line, "-technology (u) %lf", &(F_sz_um));
      F_sz_nm = F_sz_um * 1000;
      continue;
    }

    if (!strncmp("-hp Vdd", line, strlen("-hp Vdd"))) {
      sscanf(line, "-hp Vdd%[^\"]\"%[^\"]\"", jk, temp_var);
      if (!strncmp("default", temp_var, sizeof("default"))) {
        specific_hp_vdd = false;
        hp_Vdd = 1.0; /*
                       * if this is by default, then the vdd value in g_ip here
                       * does not matter
                       */
      }
      else {
        specific_hp_vdd = true;
        sscanf(line, "-hp Vdd (V) %lf", &(hp_Vdd));
      }
      continue;
    }

    if (!strncmp("-lstp Vdd", line, strlen("-lstp Vdd"))) {
      sscanf(line, "-lstp Vdd%[^\"]\"%[^\"]\"", jk, temp_var);
      if (!strncmp("default", temp_var, sizeof("default"))) {
        specific_lstp_vdd = false;
        lstp_Vdd = 1.0; /*
                         * if this is by default, then the vdd value in g_ip
                         * here does not matter
                         */
      }
      else {
        specific_lstp_vdd = true;
        sscanf(line, "-lstp Vdd (V) %lf", &(lstp_Vdd));
      }
      continue;
    }

    if (!strncmp("-lop Vdd", line, strlen("-lop Vdd"))) {
      sscanf(line, "-lop Vdd%[^\"]\"%[^\"]\"", jk, temp_var);
      if (!strncmp("default", temp_var, sizeof("default"))) {
        specific_lop_vdd = false;
        lop_Vdd = 1.0; /*
                        * if this is by default, then the vdd value in g_ip here
                        * does not matter
                        */
      }
      else {
        specific_lop_vdd = true;
        sscanf(line, "-lop Vdd (V) %lf", &(lop_Vdd));
      }
      continue;
    }

    if (!strncmp("-DVS(V):", line, strlen("-DVS(V):"))) {
      memmove(line, line + 9, strlen(line));
      while (1 == sscanf(data, "%lf%n", &temp_double, &offset)) {
        data += offset;
        dvs_voltage.push_back(temp_double);
      }
      //    	dvs_levels = dvs_voltage.size();
      continue;
    }

    if (!strncmp("-Powergating voltage", line,
                 strlen("-Powergating voltage"))) {
      sscanf(line, "-Powergating voltage%[^\"]\"%[^\"]\"", jk, temp_var);
      if (!strncmp("default", temp_var, sizeof("default"))) {
        specific_vcc_min = false;
        user_defined_vcc_min = 1.0; /*
                                     * if this is by default, then the vdd value
                                     * in g_ip here does not matter
                                     */
      }
      else {
        specific_vcc_min = true;
        sscanf(line, "-Powergating voltage (V) %lf", &(user_defined_vcc_min));
      }
      continue;
    }

    if (!strncmp("-output/input", line, strlen("-output/input"))) {
      sscanf(line, "-output/input bus %[(:-~)*]%d", jk, &(out_w));
      continue;
    }

    if (!strncmp("-operating temperature", line,
                 strlen("-operating temperature"))) {
      sscanf(line, "-operating temperature %[(:-~)*]%d", jk, &(temp));
      continue;
    }

    if (!strncmp("-cache type", line, strlen("-cache type"))) {
      sscanf(line, "-cache type%[^\"]\"%[^\"]\"", jk, temp_var);

      if (!strncmp("cache", temp_var, sizeof("cache"))) {
        is_cache = true;
      }
      else {
        is_cache = false;
      }

      if (!strncmp("main memory", temp_var, sizeof("main memory"))) {
        is_main_mem = true;
      }
      else {
        is_main_mem = false;
      }

      if (!strncmp("cam", temp_var, sizeof("cam"))) {
        pure_cam = true;
      }
      else {
        pure_cam = false;
      }

      if (!strncmp("ram", temp_var, sizeof("ram"))) {
        pure_ram = true;
      }
      else {
        if (!is_main_mem)
          pure_ram = false;
        else
          pure_ram = true;
      }

      continue;
    }

    if (!strncmp("-tag size", line, strlen("-tag size"))) {
      sscanf(line, "-tag size%[^\"]\"%[^\"]\"", jk, temp_var);
      if (!strncmp("default", temp_var, sizeof("default"))) {
        specific_tag = false;
        tag_w =
            42; /* the acutal value is calculated
                 * later based on the cache size, bank count, and associativity
                 */
      }
      else {
        specific_tag = true;
        sscanf(line, "-tag size (b) %d", &(tag_w));
      }
      continue;
    }

    if (!strncmp("-access mode", line, strlen("-access mode"))) {
      sscanf(line, "-access %[^\"]\"%[^\"]\"", jk, temp_var);
      if (!strncmp("fast", temp_var, strlen("fast"))) {
        access_mode = 2;
      }
      else if (!strncmp("sequential", temp_var, strlen("sequential"))) {
        access_mode = 1;
      }
      else if (!strncmp("normal", temp_var, strlen("normal"))) {
        access_mode = 0;
      }
      else {
        cout << "ERROR: Invalid access mode!\n";
        exit(0);
      }
      continue;
    }

    if (!strncmp("-Data array cell type", line,
                 strlen("-Data array cell type"))) {
      sscanf(line, "-Data array cell type %[^\"]\"%[^\"]\"", jk, temp_var);

      if (!strncmp("itrs-hp", temp_var, strlen("itrs-hp"))) {
        data_arr_ram_cell_tech_type = 0;
      }
      else if (!strncmp("itrs-lstp", temp_var, strlen("itrs-lstp"))) {
        data_arr_ram_cell_tech_type = 1;
      }
      else if (!strncmp("itrs-lop", temp_var, strlen("itrs-lop"))) {
        data_arr_ram_cell_tech_type = 2;
      }
      else if (!strncmp("lp-dram", temp_var, strlen("lp-dram"))) {
        data_arr_ram_cell_tech_type = 3;
      }
      else if (!strncmp("comm-dram", temp_var, strlen("comm-dram"))) {
        data_arr_ram_cell_tech_type = 4;
      }
      else {
        cout << "ERROR: Invalid type!\n";
        exit(0);
      }
      continue;
    }

    if (!strncmp("-Data array peripheral type", line,
                 strlen("-Data array peripheral type"))) {
      sscanf(line, "-Data array peripheral type %[^\"]\"%[^\"]\"", jk,
             temp_var);

      if (!strncmp("itrs-hp", temp_var, strlen("itrs-hp"))) {
        data_arr_peri_global_tech_type = 0;
      }
      else if (!strncmp("itrs-lstp", temp_var, strlen("itrs-lstp"))) {
        data_arr_peri_global_tech_type = 1;
      }
      else if (!strncmp("itrs-lop", temp_var, strlen("itrs-lop"))) {
        data_arr_peri_global_tech_type = 2;
      }
      else {
        cout << "ERROR: Invalid type!\n";
        exit(0);
      }
      continue;
    }

    if (!strncmp("-Tag array cell type", line,
                 strlen("-Tag array cell type"))) {
      sscanf(line, "-Tag array cell type %[^\"]\"%[^\"]\"", jk, temp_var);

      if (!strncmp("itrs-hp", temp_var, strlen("itrs-hp"))) {
        tag_arr_ram_cell_tech_type = 0;
      }
      else if (!strncmp("itrs-lstp", temp_var, strlen("itrs-lstp"))) {
        tag_arr_ram_cell_tech_type = 1;
      }
      else if (!strncmp("itrs-lop", temp_var, strlen("itrs-lop"))) {
        tag_arr_ram_cell_tech_type = 2;
      }
      else if (!strncmp("lp-dram", temp_var, strlen("lp-dram"))) {
        tag_arr_ram_cell_tech_type = 3;
      }
      else if (!strncmp("comm-dram", temp_var, strlen("comm-dram"))) {
        tag_arr_ram_cell_tech_type = 4;
      }
      else {
        cout << "ERROR: Invalid type!\n";
        exit(0);
      }
      continue;
    }

    if (!strncmp("-Tag array peripheral type", line,
                 strlen("-Tag array peripheral type"))) {
      sscanf(line, "-Tag array peripheral type %[^\"]\"%[^\"]\"", jk, temp_var);

      if (!strncmp("itrs-hp", temp_var, strlen("itrs-hp"))) {
        tag_arr_peri_global_tech_type = 0;
      }
      else if (!strncmp("itrs-lstp", temp_var, strlen("itrs-lstp"))) {
        tag_arr_peri_global_tech_type = 1;
      }
      else if (!strncmp("itrs-lop", temp_var, strlen("itrs-lop"))) {
        tag_arr_peri_global_tech_type = 2;
      }
      else {
        cout << "ERROR: Invalid type!\n";
        exit(0);
      }
      continue;
    }
    if (!strncmp("-design", line, strlen("-design"))) {
      sscanf(line, "-%[((:-~)| |,)*]%d:%d:%d:%d:%d", jk, &(delay_wt),
             &(dynamic_power_wt), &(leakage_power_wt), &(cycle_time_wt),
             &(area_wt));
      continue;
    }

    if (!strncmp("-deviate", line, strlen("-deviate"))) {
      sscanf(line, "-%[((:-~)| |,)*]%d:%d:%d:%d:%d", jk, &(delay_dev),
             &(dynamic_power_dev), &(leakage_power_dev), &(cycle_time_dev),
             &(area_dev));
      continue;
    }

    if (!strncmp("-Optimize", line, strlen("-Optimize"))) {
      sscanf(line, "-Optimize  %[^\"]\"%[^\"]\"", jk, temp_var);

      if (!strncmp("ED^2", temp_var, strlen("ED^2"))) {
        ed = 2;
      }
      else if (!strncmp("ED", temp_var, strlen("ED"))) {
        ed = 1;
      }
      else {
        ed = 0;
      }
    }

    if (!strncmp("-NUCAdesign", line, strlen("-NUCAdesign"))) {
      sscanf(line, "-%[((:-~)| |,)*]%d:%d:%d:%d:%d", jk, &(delay_wt_nuca),
             &(dynamic_power_wt_nuca), &(leakage_power_wt_nuca),
             &(cycle_time_wt_nuca), &(area_wt_nuca));
      continue;
    }

    if (!strncmp("-NUCAdeviate", line, strlen("-NUCAdeviate"))) {
      sscanf(line, "-%[((:-~)| |,)*]%d:%d:%d:%d:%d", jk, &(delay_dev_nuca),
             &(dynamic_power_dev_nuca), &(leakage_power_dev_nuca),
             &(cycle_time_dev_nuca), &(area_dev_nuca));
      continue;
    }

    if (!strncmp("-Cache model", line, strlen("-cache model"))) {
      sscanf(line, "-Cache model %[^\"]\"%[^\"]\"", jk, temp_var);

      if (!strncmp("UCA", temp_var, strlen("UCA"))) {
        nuca = 0;
      }
      else {
        nuca = 1;
      }
      continue;
    }

    if (!strncmp("-NUCA bank", line, strlen("-NUCA bank"))) {
      sscanf(line, "-NUCA bank count %d", &(nuca_bank_count));

      if (nuca_bank_count != 0) {
        force_nuca_bank = 1;
      }
      continue;
    }

    if (!strncmp("-Wire inside mat", line, strlen("-Wire inside mat"))) {
      sscanf(line, "-Wire%[^\"]\"%[^\"]\"", jk, temp_var);

      if (!strncmp("global", temp_var, strlen("global"))) {
        wire_is_mat_type = 2;
        continue;
      }
      else if (!strncmp("local", temp_var, strlen("local"))) {
        wire_is_mat_type = 0;
        continue;
      }
      else {
        wire_is_mat_type = 1;
        continue;
      }
    }

    if (!strncmp("-Wire outside mat", line, strlen("-Wire outside mat"))) {
      sscanf(line, "-Wire%[^\"]\"%[^\"]\"", jk, temp_var);

      if (!strncmp("global", temp_var, strlen("global"))) {
        wire_os_mat_type = 2;
      }
      else {
        wire_os_mat_type = 1;
      }
      continue;
    }

    if (!strncmp("-Interconnect projection", line,
                 strlen("-Interconnect projection"))) {
      sscanf(line, "-Interconnect projection%[^\"]\"%[^\"]\"", jk, temp_var);

      if (!strncmp("aggressive", temp_var, strlen("aggressive"))) {
        ic_proj_type = 0;
      }
      else {
        ic_proj_type = 1;
      }
      continue;
    }

    if (!strncmp("-Wire signalling", line, strlen("-wire signalling"))) {
      sscanf(line, "-Wire%[^\"]\"%[^\"]\"", jk, temp_var);

      if (!strncmp("default", temp_var, strlen("default"))) {
        force_wiretype = 0;
        wt = Global;
      }
      else if (!(strncmp("Global_10", temp_var, strlen("Global_10")))) {
        force_wiretype = 1;
        wt = Global_10;
      }
      else if (!(strncmp("Global_20", temp_var, strlen("Global_20")))) {
        force_wiretype = 1;
        wt = Global_20;
      }
      else if (!(strncmp("Global_30", temp_var, strlen("Global_30")))) {
        force_wiretype = 1;
        wt = Global_30;
      }
      else if (!(strncmp("Global_5", temp_var, strlen("Global_5")))) {
        force_wiretype = 1;
        wt = Global_5;
      }
      else if (!(strncmp("Global", temp_var, strlen("Global")))) {
        force_wiretype = 1;
        wt = Global;
      }
      else {
        wt = Low_swing;
        force_wiretype = 1;
      }
      continue;
    }

    if (!strncmp("-Core", line, strlen("-Core"))) {
      sscanf(line, "-Core count %d\n", &(cores));
      if (cores > 16) {
        printf("No. of cores should be less than 16!\n");
      }
      continue;
    }

    if (!strncmp("-Cache level", line, strlen("-Cache level"))) {
      sscanf(line, "-Cache l%[^\"]\"%[^\"]\"", jk, temp_var);
      if (!strncmp("L2", temp_var, strlen("L2"))) {
        cache_level = 0;
      }
      else {
        cache_level = 1;
      }
    }

    if (!strncmp("-Print level", line, strlen("-Print level"))) {
      sscanf(line, "-Print l%[^\"]\"%[^\"]\"", jk, temp_var);
      if (!strncmp("DETAILED", temp_var, strlen("DETAILED"))) {
        print_detail = 1;
      }
      else {
        print_detail = 0;
      }
    }
    if (!strncmp("-Add ECC", line, strlen("-Add ECC"))) {
      sscanf(line, "-Add ECC %[^\"]\"%[^\"]\"", jk, temp_var);
      if (!strncmp("true", temp_var, strlen("true"))) {
        add_ecc_b_ = true;
      }
      else {
        add_ecc_b_ = false;
      }
    }

    if (!strncmp("-CLDriver vertical", line, strlen("-CLDriver vertical"))) {
      sscanf(line, "-CLDriver vertical %[^\"]\"%[^\"]\"", jk, temp_var);
      if (!strncmp("true", temp_var, strlen("true"))) {
        cl_vertical = true;
      }
      else {
        cl_vertical = false;
      }
    }

    if (!strncmp("-Array Power Gating", line, strlen("-Array Power Gating"))) {
      sscanf(line, "-Array Power Gating %[^\"]\"%[^\"]\"", jk, temp_var);
      if (!strncmp("true", temp_var, strlen("true"))) {
        array_power_gated = true;
      }
      else {
        array_power_gated = false;
      }
    }

    if (!strncmp("-Bitline floating", line, strlen("-Bitline floating"))) {
      sscanf(line, "-Bitline floating %[^\"]\"%[^\"]\"", jk, temp_var);
      if (!strncmp("true", temp_var, strlen("true"))) {
        bitline_floating = true;
      }
      else {
        bitline_floating = false;
      }
    }

    if (!strncmp("-WL Power Gating", line, strlen("-WL Power Gating"))) {
      sscanf(line, "-WL Power Gating %[^\"]\"%[^\"]\"", jk, temp_var);
      if (!strncmp("true", temp_var, strlen("true"))) {
        wl_power_gated = true;
      }
      else {
        wl_power_gated = false;
      }
    }

    if (!strncmp("-CL Power Gating", line, strlen("-CL Power Gating"))) {
      sscanf(line, "-CL Power Gating %[^\"]\"%[^\"]\"", jk, temp_var);
      if (!strncmp("true", temp_var, strlen("true"))) {
        cl_power_gated = true;
      }
      else {
        cl_power_gated = false;
      }
    }

    if (!strncmp("-Interconnect Power Gating", line,
                 strlen("-Interconnect Power Gating"))) {
      sscanf(line, "-Interconnect Power Gating %[^\"]\"%[^\"]\"", jk, temp_var);
      if (!strncmp("true", temp_var, strlen("true"))) {
        interconect_power_gated = true;
      }
      else {
        interconect_power_gated = false;
      }
    }

    if (!strncmp("-Power Gating Performance Loss", line,
                 strlen("-Power Gating Performance Loss"))) {
      sscanf(line, "-Power Gating Performance Loss %lf", &(perfloss));
      continue;
    }

    if (!strncmp("-Power Gating", line, strlen("-Power Gating"))) {
      sscanf(line, "-Power Gating %[^\"]\"%[^\"]\"", jk, temp_var);
      if (!strncmp("true", temp_var, strlen("true"))) {
        power_gating = true;
      }
      else {
        power_gating = false;
      }
    }

    if (!strncmp("-Long channel devices", line,
                 strlen("-Long channel devices"))) {
      sscanf(line, "-Long channel devices %[^\"]\"%[^\"]\"", jk, temp_var);
      if (!strncmp("true", temp_var, strlen("true"))) {
        long_channel_device = true;
      }
      else {
        long_channel_device = false;
      }
    }

    if (!strncmp("-Print input parameters", line,
                 strlen("-Print input parameters"))) {
      sscanf(line, "-Print input %[^\"]\"%[^\"]\"", jk, temp_var);
      if (!strncmp("true", temp_var, strlen("true"))) {
        print_input_args = true;
      }
      else {
        print_input_args = false;
      }
    }

    if (!strncmp("-Force cache config", line, strlen("-Force cache config"))) {
      sscanf(line, "-Force cache %[^\"]\"%[^\"]\"", jk, temp_var);
      if (!strncmp("true", temp_var, strlen("true"))) {
        force_cache_config = true;
      }
      else {
        force_cache_config = false;
      }
    }

    if (!strncmp("-Ndbl", line, strlen("-Ndbl"))) {
      sscanf(line, "-Ndbl %d\n", &(ndbl));
      continue;
    }
    if (!strncmp("-Ndwl", line, strlen("-Ndwl"))) {
      sscanf(line, "-Ndwl %d\n", &(ndwl));
      continue;
    }
    if (!strncmp("-Nspd", line, strlen("-Nspd"))) {
      sscanf(line, "-Nspd %d\n", &(nspd));
      continue;
    }
    if (!strncmp("-Ndsam1", line, strlen("-Ndsam1"))) {
      sscanf(line, "-Ndsam1 %d\n", &(ndsam1));
      continue;
    }
    if (!strncmp("-Ndsam2", line, strlen("-Ndsam2"))) {
      sscanf(line, "-Ndsam2 %d\n", &(ndsam2));
      continue;
    }
    if (!strncmp("-Ndcm", line, strlen("-Ndcm"))) {
      sscanf(line, "-Ndcm %d\n", &(ndcm));
      continue;
    }
  }
  rpters_in_htree = true;
  fclose(fp);
}

void InputParameter::display_ip() {
  cout << "Cache size                    : " << cache_sz << endl;
  cout << "Block size                    : " << line_sz << endl;
  cout << "Associativity                 : " << assoc << endl;
  cout << "Read only ports               : " << num_rd_ports << endl;
  cout << "Write only ports              : " << num_wr_ports << endl;
  cout << "Read write ports              : " << num_rw_ports << endl;
  cout << "Single ended read ports       : " << num_se_rd_ports << endl;
  if (fully_assoc || pure_cam) {
    cout << "Search ports                  : " << num_search_ports << endl;
  }
  cout << "Cache banks (UCA)             : " << nbanks << endl;
  cout << "Technology                    : " << F_sz_um << endl;
  cout << "User specified HP   Vdd (v)?  : " << std::boolalpha
       << specific_hp_vdd << endl;
  if (specific_hp_vdd) {
    cout << "User defined HP Vdd (v)       : " << hp_Vdd << endl;
  }
  cout << "User specified LSTP Vdd (v)?  : " << std::boolalpha
       << specific_lstp_vdd << endl;
  if (specific_lstp_vdd) {
    cout << "User defined HP Vdd (v)       : " << lstp_Vdd << endl;
  }
  cout << "User specified LOP  Vdd (v)?  : " << std::boolalpha
       << specific_lop_vdd << endl;
  if (specific_lop_vdd) {
    cout << "User defined HP Vdd (v)       : " << lop_Vdd << endl;
  }
  cout << "Temperature                   : " << temp << endl;
  cout << "Tag size                      : " << tag_w << endl;
  if (is_cache) {
    cout << "array type                    : "
         << "Cache" << endl;
  }
  if (pure_ram) {
    cout << "array type                    : "
         << "Scratch RAM" << endl;
  }
  if (pure_cam) {
    cout << "array type                    : "
         << "CAM" << endl;
  }
  cout << "Model as memory               : " << is_main_mem << endl;
  cout << "Access mode                   : " << access_mode << endl;
  cout << "Data array cell type          : " << data_arr_ram_cell_tech_type
       << endl;
  cout << "Data array peripheral type    : " << data_arr_peri_global_tech_type
       << endl;
  cout << "Tag array cell type           : " << tag_arr_ram_cell_tech_type
       << endl;
  cout << "Tag array peripheral type     : " << tag_arr_peri_global_tech_type
       << endl;
  cout << "Optimization target           : " << ed << endl;
  cout << "Design objective (UCA wt)     : " << delay_wt << " "
       << dynamic_power_wt << " " << leakage_power_wt << " " << cycle_time_wt
       << " " << area_wt << endl;
  cout << "Design objective (UCA dev)    : " << delay_dev << " "
       << dynamic_power_dev << " " << leakage_power_dev << " " << cycle_time_dev
       << " " << area_dev << endl;
  if (nuca) {
    cout << "Cores                         : " << cores << endl;

    cout << "Design objective (NUCA wt)    : " << delay_wt_nuca << " "
         << dynamic_power_wt_nuca << " " << leakage_power_wt_nuca << " "
         << cycle_time_wt_nuca << " " << area_wt_nuca << endl;
    cout << "Design objective (NUCA dev)   : " << delay_dev_nuca << " "
         << dynamic_power_dev_nuca << " " << leakage_power_dev_nuca << " "
         << cycle_time_dev_nuca << " " << area_dev_nuca << endl;
  }
  cout << "Cache model                   : " << nuca << endl;
  cout << "Nuca bank                     : " << nuca_bank_count << endl;
  cout << "Wire inside mat               : " << wire_is_mat_type << endl;
  cout << "Wire outside mat              : " << wire_os_mat_type << endl;
  cout << "Interconnect projection       : " << ic_proj_type << endl;
  cout << "Wire signalling               : " << force_wiretype << endl;
  cout << "Print level                   : " << print_detail << endl;
  cout << "ECC overhead                  : " << add_ecc_b_ << endl;
  cout << "Page size                     : " << page_sz_bits << endl;
  cout << "Burst length                  : " << burst_len << endl;
  cout << "Internal prefetch width       : " << int_prefetch_w << endl;
  cout << "Force cache config            : " << g_ip->force_cache_config
       << endl;
  if (g_ip->force_cache_config) {
    cout << "Ndwl                          : " << g_ip->ndwl << endl;
    cout << "Ndbl                          : " << g_ip->ndbl << endl;
    cout << "Nspd                          : " << g_ip->nspd << endl;
    cout << "Ndcm                          : " << g_ip->ndcm << endl;
    cout << "Ndsam1                        : " << g_ip->ndsam1 << endl;
    cout << "Ndsam2                        : " << g_ip->ndsam2 << endl;
  }
  // cout << "Placing subarray out driver vertical?     : " << g_ip->cl_vertical
  // << endl;
}

powerComponents operator+(const powerComponents &x, const powerComponents &y) {
  powerComponents z;

  z.dynamic = x.dynamic + y.dynamic;
  z.leakage = x.leakage + y.leakage;
  z.gate_leakage = x.gate_leakage + y.gate_leakage;
  z.short_circuit = x.short_circuit + y.short_circuit;
  z.longer_channel_leakage =
      x.longer_channel_leakage + y.longer_channel_leakage;
  z.power_gated_leakage = x.power_gated_leakage + y.power_gated_leakage;
  z.power_gated_with_long_channel_leakage =
      x.power_gated_with_long_channel_leakage +
      y.power_gated_with_long_channel_leakage;

  return z;
}

powerComponents operator*(const powerComponents &x, double const *const y) {
  powerComponents z;

  z.dynamic = x.dynamic * y[0];
  z.leakage = x.leakage * y[1];
  z.gate_leakage = x.gate_leakage * y[2];
  z.short_circuit = x.short_circuit * y[3];
  z.longer_channel_leakage =
      x.longer_channel_leakage *
      y[1];  // longer channel leakage has the same behavior as normal leakage
  z.power_gated_leakage =
      x.power_gated_leakage *
      y[1];  // power_gated_leakage has the same behavior as normal leakage
  z.power_gated_with_long_channel_leakage =
      x.power_gated_with_long_channel_leakage *
      y[1];  // power_gated_with_long_channel_leakage has the same behavior as
             // normal leakage
  return z;
}

powerDef operator+(const powerDef &x, const powerDef &y) {
  powerDef z;

  z.readOp = x.readOp + y.readOp;
  z.writeOp = x.writeOp + y.writeOp;
  z.searchOp = x.searchOp + y.searchOp;
  return z;
}

powerDef operator*(const powerDef &x, double const *const y) {
  powerDef z;

  z.readOp = x.readOp * y;
  z.writeOp = x.writeOp * y;
  z.searchOp = x.searchOp * y;
  return z;
}

uca_org_t cacti_interface(const string &infile_name) {
  uca_org_t fin_res;
  // uca_org_t result;
  fin_res.valid = false;

  g_ip = new InputParameter();
  g_ip->parse_cfg(infile_name);
  if (!g_ip->error_checking())
    exit(0);
  if (g_ip->print_input_args)
    g_ip->display_ip();

  init_tech_params(g_ip->F_sz_um, false);  // this init is for initializing
                                           // wires
  Wire winit;  // Do not delete this line. It initializes wires.
               //  g_tp.peri_global.display();
               //  g_tp.sram_cell.display();

  //  For HighRadix Only
  //  ////  Wire wirea(g_ip->wt, 1000);
  //  ////  wirea.print_wire();
  //  ////  cout << "Wire Area " << wirea.area.get_area() << " sq. u" << endl;
  //  //  winit.print_wire();
  //  //
  //    HighRadix *hr;
  //      hr = new HighRadix();
  //      hr->compute_power();
  //      hr->print_router();
  //    exit(0);
  //
  //    double sub_switch_sz = 2;
  //    double rows = 32;
  //    for (int i=0; i<6; i++) {
  //      sub_switch_sz = pow(2, i);
  //      rows = 64/sub_switch_sz;
  //      hr = new HighRadix(sub_switch_sz, rows, .8/* freq */, 64, 2, 64, 0.7);
  //      hr->compute_power();
  //      hr->print_router();
  //      delete hr;
  //    }
  //  //  HighRadix yarc;
  //  //  yarc.compute_power();
  //  //  yarc.print_router();
  //    winit.print_wire();
  //    exit(0);
  //  For HighRadix Only End

  if (g_ip->nuca == 1) {
    Nuca n(&g_tp.peri_global);
    n.sim_nuca();
  }

  // g_ip->display_ip();

  solve(&fin_res);
  //  output_UCA(&fin_res);
  //  Wire::print_wire();
  output_data_csv(fin_res);

  if (!g_ip->dvs_voltage.empty()) {
    update_dvs(&fin_res);
  }
  if (g_ip->power_gating) {
    update_pg(&fin_res);  // this is needed for compute area overhead of
                          // power-gating, even the gated power is calculated
                          // together un-gated leakage
  }
  output_UCA(&fin_res);

  Wire wprint;  // reset wires to original configuration as in *.cfg file (dvs
                // level 0)
  Wire::print_wire();

  delete (g_ip);
  return fin_res;
}

// cacti6.5's plain interface, please keep !!!
uca_org_t cacti_interface(
    int cache_size, int line_size, int associativity, int rw_ports,
    int excl_read_ports, int excl_write_ports, int single_ended_read_ports,
    int banks,
    double tech_node,  // in nm
    int page_sz, int burst_length, int pre_width, int output_width,
    int specific_tag, int tag_width,
    int access_mode,  // 0 normal, 1 seq, 2 fast
    int cache,        // scratch ram or cache
    int main_mem, int obj_func_delay, int obj_func_dynamic_power,
    int obj_func_leakage_power, int obj_func_area, int obj_func_cycle_time,
    int dev_func_delay, int dev_func_dynamic_power, int dev_func_leakage_power,
    int dev_func_area, int dev_func_cycle_time,
    int ed_ed2_none,  // 0 - ED, 1 - ED^2, 2 - use weight and deviate
    int temp,
    int wt,  // 0 - default(search across everything), 1 - global, 2 - 5% delay
             // penalty, 3 - 10%, 4 - 20 %, 5 - 30%, 6 - low-swing
    int data_arr_ram_cell_tech_flavor_in,  // 0-4
    int data_arr_peri_global_tech_flavor_in,
    int tag_arr_ram_cell_tech_flavor_in, int tag_arr_peri_global_tech_flavor_in,
    int interconnect_projection_type_in,  // 0 - aggressive, 1 - normal
    int wire_inside_mat_type_in, int wire_outside_mat_type_in,
    int is_nuca,  // 0 - UCA, 1 - NUCA
    int core_count,
    int cache_level,  // 0 - L2, 1 - L3
    int nuca_bank_count, int nuca_obj_func_delay,
    int nuca_obj_func_dynamic_power, int nuca_obj_func_leakage_power,
    int nuca_obj_func_area, int nuca_obj_func_cycle_time,
    int nuca_dev_func_delay, int nuca_dev_func_dynamic_power,
    int nuca_dev_func_leakage_power, int nuca_dev_func_area,
    int nuca_dev_func_cycle_time,
    int REPEATERS_IN_HTREE_SEGMENTS_in,  // TODO for now only wires with
                                         // repeaters are supported
    int p_input) {
  g_ip = new InputParameter();
  g_ip->add_ecc_b_ = true;

  g_ip->data_arr_ram_cell_tech_type = data_arr_ram_cell_tech_flavor_in;
  g_ip->data_arr_peri_global_tech_type = data_arr_peri_global_tech_flavor_in;
  g_ip->tag_arr_ram_cell_tech_type = tag_arr_ram_cell_tech_flavor_in;
  g_ip->tag_arr_peri_global_tech_type = tag_arr_peri_global_tech_flavor_in;

  g_ip->ic_proj_type = interconnect_projection_type_in;
  g_ip->wire_is_mat_type = wire_inside_mat_type_in;
  g_ip->wire_os_mat_type = wire_outside_mat_type_in;
  g_ip->burst_len = burst_length;
  g_ip->int_prefetch_w = pre_width;
  g_ip->page_sz_bits = page_sz;

  g_ip->cache_sz = cache_size;
  g_ip->line_sz = line_size;
  g_ip->assoc = associativity;
  g_ip->nbanks = banks;
  g_ip->out_w = output_width;
  g_ip->specific_tag = specific_tag;
  if (tag_width == 0) {
    g_ip->tag_w = 42;
  }
  else {
    g_ip->tag_w = tag_width;
  }

  g_ip->access_mode = access_mode;
  g_ip->delay_wt = obj_func_delay;
  g_ip->dynamic_power_wt = obj_func_dynamic_power;
  g_ip->leakage_power_wt = obj_func_leakage_power;
  g_ip->area_wt = obj_func_area;
  g_ip->cycle_time_wt = obj_func_cycle_time;
  g_ip->delay_dev = dev_func_delay;
  g_ip->dynamic_power_dev = dev_func_dynamic_power;
  g_ip->leakage_power_dev = dev_func_leakage_power;
  g_ip->area_dev = dev_func_area;
  g_ip->cycle_time_dev = dev_func_cycle_time;
  g_ip->ed = ed_ed2_none;

  switch (wt) {
    case (0):
      g_ip->force_wiretype = 0;
      g_ip->wt = Global;
      break;
    case (1):
      g_ip->force_wiretype = 1;
      g_ip->wt = Global;
      break;
    case (2):
      g_ip->force_wiretype = 1;
      g_ip->wt = Global_5;
      break;
    case (3):
      g_ip->force_wiretype = 1;
      g_ip->wt = Global_10;
      break;
    case (4):
      g_ip->force_wiretype = 1;
      g_ip->wt = Global_20;
      break;
    case (5):
      g_ip->force_wiretype = 1;
      g_ip->wt = Global_30;
      break;
    case (6):
      g_ip->force_wiretype = 1;
      g_ip->wt = Low_swing;
      break;
    default:
      cout << "Unknown wire type!\n";
      exit(0);
  }

  g_ip->delay_wt_nuca = nuca_obj_func_delay;
  g_ip->dynamic_power_wt_nuca = nuca_obj_func_dynamic_power;
  g_ip->leakage_power_wt_nuca = nuca_obj_func_leakage_power;
  g_ip->area_wt_nuca = nuca_obj_func_area;
  g_ip->cycle_time_wt_nuca = nuca_obj_func_cycle_time;
  g_ip->delay_dev_nuca = dev_func_delay;
  g_ip->dynamic_power_dev_nuca = nuca_dev_func_dynamic_power;
  g_ip->leakage_power_dev_nuca = nuca_dev_func_leakage_power;
  g_ip->area_dev_nuca = nuca_dev_func_area;
  g_ip->cycle_time_dev_nuca = nuca_dev_func_cycle_time;
  g_ip->nuca = is_nuca;
  g_ip->nuca_bank_count = nuca_bank_count;
  if (nuca_bank_count > 0) {
    g_ip->force_nuca_bank = 1;
  }
  g_ip->cores = core_count;
  g_ip->cache_level = cache_level;

  g_ip->temp = temp;

  g_ip->F_sz_nm = tech_node;
  g_ip->F_sz_um = tech_node / 1000;
  g_ip->is_main_mem = (main_mem != 0) ? true : false;
  g_ip->is_cache = (cache != 0) ? true : false;
  g_ip->rpters_in_htree = (REPEATERS_IN_HTREE_SEGMENTS_in != 0) ? true : false;

  g_ip->num_rw_ports = rw_ports;
  g_ip->num_rd_ports = excl_read_ports;
  g_ip->num_wr_ports = excl_write_ports;
  g_ip->num_se_rd_ports = single_ended_read_ports;
  g_ip->print_detail = 1;
  g_ip->nuca = 0;

  g_ip->wt = Global_5;
  g_ip->force_cache_config = false;
  g_ip->force_wiretype = false;
  g_ip->print_input_args = p_input;

  uca_org_t fin_res;
  fin_res.valid = false;

  if (g_ip->error_checking() == false)
    exit(0);
  if (g_ip->print_input_args)
    g_ip->display_ip();
  init_tech_params(g_ip->F_sz_um, false);
  Wire winit;  // Do not delete this line. It initializes wires.

  if (g_ip->nuca == 1) {
    Nuca n(&g_tp.peri_global);
    n.sim_nuca();
  }
  solve(&fin_res);

  output_UCA(&fin_res);

  delete (g_ip);
  return fin_res;
}

// McPAT's plain interface, please keep !!!
uca_org_t cacti_interface(
    int cache_size, int line_size, int associativity, int rw_ports,
    int excl_read_ports,  // para5
    int excl_write_ports, int single_ended_read_ports, int search_ports,
    int banks,
    double tech_node,  // para10
    int output_width, int specific_tag, int tag_width, int access_mode,
    int cache,  // para15
    int main_mem, int obj_func_delay, int obj_func_dynamic_power,
    int obj_func_leakage_power,
    int obj_func_cycle_time,  // para20
    int obj_func_area, int dev_func_delay, int dev_func_dynamic_power,
    int dev_func_leakage_power,
    int dev_func_area,  // para25
    int dev_func_cycle_time,
    int ed_ed2_none,  // 0 - ED, 1 - ED^2, 2 - use weight and deviate
    int temp,
    int wt,  // 0 - default(search across everything), 1 - global, 2 - 5% delay
             // penalty, 3 - 10%, 4 - 20 %, 5 - 30%, 6 - low-swing
    int data_arr_ram_cell_tech_flavor_in,  // para30
    int data_arr_peri_global_tech_flavor_in,
    int tag_arr_ram_cell_tech_flavor_in, int tag_arr_peri_global_tech_flavor_in,
    int interconnect_projection_type_in,
    int wire_inside_mat_type_in,  // para35
    int wire_outside_mat_type_in, int REPEATERS_IN_HTREE_SEGMENTS_in,
    int VERTICAL_HTREE_WIRES_OVER_THE_ARRAY_in,
    int BROADCAST_ADDR_DATAIN_OVER_VERTICAL_HTREES_in,
    int PAGE_SIZE_BITS_in,  // para40
    int BURST_LENGTH_in, int INTERNAL_PREFETCH_WIDTH_in, int force_wiretype,
    int wiretype,
    int force_config,  // para45
    int ndwl, int ndbl, int nspd, int ndcm,
    int ndsam1,  // para50
    int ndsam2, int ecc) {
  g_ip = new InputParameter();

  uca_org_t fin_res;
  fin_res.valid = false;

  g_ip->data_arr_ram_cell_tech_type = data_arr_ram_cell_tech_flavor_in;
  g_ip->data_arr_peri_global_tech_type = data_arr_peri_global_tech_flavor_in;
  g_ip->tag_arr_ram_cell_tech_type = tag_arr_ram_cell_tech_flavor_in;
  g_ip->tag_arr_peri_global_tech_type = tag_arr_peri_global_tech_flavor_in;

  g_ip->ic_proj_type = interconnect_projection_type_in;
  g_ip->wire_is_mat_type = wire_inside_mat_type_in;
  g_ip->wire_os_mat_type = wire_outside_mat_type_in;
  g_ip->burst_len = BURST_LENGTH_in;
  g_ip->int_prefetch_w = INTERNAL_PREFETCH_WIDTH_in;
  g_ip->page_sz_bits = PAGE_SIZE_BITS_in;

  g_ip->cache_sz = cache_size;
  g_ip->line_sz = line_size;
  g_ip->assoc = associativity;
  g_ip->nbanks = banks;
  g_ip->out_w = output_width;
  g_ip->specific_tag = specific_tag;
  if (specific_tag == 0) {
    g_ip->tag_w = 42;
  }
  else {
    g_ip->tag_w = tag_width;
  }

  g_ip->access_mode = access_mode;
  g_ip->delay_wt = obj_func_delay;
  g_ip->dynamic_power_wt = obj_func_dynamic_power;
  g_ip->leakage_power_wt = obj_func_leakage_power;
  g_ip->area_wt = obj_func_area;
  g_ip->cycle_time_wt = obj_func_cycle_time;
  g_ip->delay_dev = dev_func_delay;
  g_ip->dynamic_power_dev = dev_func_dynamic_power;
  g_ip->leakage_power_dev = dev_func_leakage_power;
  g_ip->area_dev = dev_func_area;
  g_ip->cycle_time_dev = dev_func_cycle_time;
  g_ip->temp = temp;
  g_ip->ed = ed_ed2_none;

  g_ip->F_sz_nm = tech_node;
  g_ip->F_sz_um = tech_node / 1000;
  g_ip->is_main_mem = (main_mem != 0) ? true : false;
  g_ip->is_cache = (cache == 1) ? true : false;
  g_ip->pure_ram = (cache == 0) ? true : false;
  g_ip->pure_cam = (cache == 2) ? true : false;
  g_ip->rpters_in_htree = (REPEATERS_IN_HTREE_SEGMENTS_in != 0) ? true : false;
  g_ip->ver_htree_wires_over_array = VERTICAL_HTREE_WIRES_OVER_THE_ARRAY_in;
  g_ip->broadcast_addr_din_over_ver_htrees =
      BROADCAST_ADDR_DATAIN_OVER_VERTICAL_HTREES_in;

  g_ip->num_rw_ports = rw_ports;
  g_ip->num_rd_ports = excl_read_ports;
  g_ip->num_wr_ports = excl_write_ports;
  g_ip->num_se_rd_ports = single_ended_read_ports;
  g_ip->num_search_ports = search_ports;

  g_ip->print_detail = 1;
  g_ip->nuca = 0;

  if (force_wiretype == 0) {
    g_ip->wt = Global;
    g_ip->force_wiretype = false;
  }
  else {
    g_ip->force_wiretype = true;
    if (wiretype == 10) {
      g_ip->wt = Global_10;
    }
    if (wiretype == 20) {
      g_ip->wt = Global_20;
    }
    if (wiretype == 30) {
      g_ip->wt = Global_30;
    }
    if (wiretype == 5) {
      g_ip->wt = Global_5;
    }
    if (wiretype == 0) {
      g_ip->wt = Low_swing;
    }
  }
  // g_ip->wt = Global_5;
  if (force_config == 0) {
    g_ip->force_cache_config = false;
  }
  else {
    g_ip->force_cache_config = true;
    g_ip->ndbl = ndbl;
    g_ip->ndwl = ndwl;
    g_ip->nspd = nspd;
    g_ip->ndcm = ndcm;
    g_ip->ndsam1 = ndsam1;
    g_ip->ndsam2 = ndsam2;
  }

  if (ecc == 0) {
    g_ip->add_ecc_b_ = false;
  }
  else {
    g_ip->add_ecc_b_ = true;
  }

  if (!g_ip->error_checking())
    exit(0);

  init_tech_params(g_ip->F_sz_um, false);
  Wire winit;  // Do not delete this line. It initializes wires.

  g_ip->display_ip();
  solve(&fin_res);
  output_UCA(&fin_res);
  output_data_csv(fin_res);
  delete (g_ip);

  return fin_res;
}

bool InputParameter::error_checking() {
  int A;
  bool seq_access = false;
  fast_access = true;
  fully_assoc = false;

  switch (access_mode) {
    case 0:
      seq_access = false;
      fast_access = false;
      break;
    case 1:
      seq_access = true;
      fast_access = false;
      break;
    case 2:
      seq_access = false;
      fast_access = true;
      break;
  }

  if (is_main_mem) {
    if (ic_proj_type == 0) {
      cerr << "DRAM model supports only conservative interconnect "
              "projection!\n\n";
      return false;
    }
  }

  uint32_t B = line_sz;

  if (B < 1) {
    cerr << "Block size must >= 1" << endl;
    return false;
  }
  else if (B * 8 < out_w) {
    cerr << "Block size must be at least " << out_w / 8 << endl;
    return false;
  }

  if (F_sz_um <= 0) {
    cerr << "Feature size must be > 0" << endl;
    return false;
  }
  else if (F_sz_um > 0.181) {
    cerr << "Feature size must be <= 180 nm" << endl;
    return false;
  }
  else if (F_sz_um > 0.091 && (data_arr_ram_cell_tech_type != itrs_hp ||
                               tag_arr_ram_cell_tech_type != itrs_hp ||
                               data_arr_peri_global_tech_type != itrs_hp ||
                               tag_arr_peri_global_tech_type != itrs_hp)) {
    cerr << "Feature size from 90nm to 180 nm only support the ITRS HP device "
            "type"
         << endl;
    return false;
  }

  uint32_t RWP = num_rw_ports;
  uint32_t ERP = num_rd_ports;
  uint32_t EWP = num_wr_ports;
  uint32_t NSER = num_se_rd_ports;
  uint32_t SCHP = num_search_ports;

  // TODO: revisit this. This is an important feature. Sheng thought this should
  // be used
  //  // If multiple banks and multiple ports are specified, then if number of
  //  ports is less than or equal to
  //  // the number of banks, we assume that the multiple ports are implemented
  //  via the multiple banks.
  //  // In such a case we assume that each bank has 1 RWP port.
  //  if ((RWP + ERP + EWP) <= nbanks && nbanks>1)
  //  {
  //    RWP  = 1;
  //    ERP  = 0;
  //    EWP  = 0;
  //    NSER = 0;
  //  }
  //  else if ((RWP < 0) || (EWP < 0) || (ERP < 0))
  //  {
  //    cerr << "Ports must >=0" << endl;
  //    return false;
  //  }
  //  else if (RWP > 2)
  //  {
  //    cerr << "Maximum of 2 read/write ports" << endl;
  //    return false;
  //  }
  //  else if ((RWP+ERP+EWP) < 1)
  // Changed to new implementation:
  // The number of ports specified at input is per bank
  if ((RWP + ERP + EWP) < 1) {
    cerr << "Must have at least one port" << endl;
    return false;
  }

  if (is_pow2(nbanks) == false) {
    cerr << "Number of subbanks should be greater than or equal to 1 and "
            "should be a power of 2"
         << endl;
    return false;
  }

  int C = cache_sz / nbanks;
  if (C < 64) {
    cerr << "Cache size must >=64" << endl;
    return false;
  }

  // TODO: revisit this
  //   if (pure_ram==true && assoc!=1)
  //    {
  //  	  cerr << "Pure RAM must have assoc as 1" << endl;
  //  	  return false;
  //    }

  // fully assoc and cam check
  if (is_cache && assoc == 0)
    fully_assoc = true;
  else
    fully_assoc = false;

  if (pure_cam == true && assoc != 0) {
    cerr << "Pure CAM must have associativity as 0" << endl;
    return false;
  }

  if (assoc == 0 && (pure_cam == false && is_cache == false)) {
    cerr << "Only CAM or Fully associative cache can have associativity as 0"
         << endl;
    return false;
  }

  if ((fully_assoc == true || pure_cam == true) &&
      (data_arr_ram_cell_tech_type != tag_arr_ram_cell_tech_type ||
       data_arr_peri_global_tech_type != tag_arr_peri_global_tech_type)) {
    cerr << "CAM and fully associative cache must have same device type for "
            "both data and tag array"
         << endl;
    return false;
  }

  if ((fully_assoc == true || pure_cam == true) &&
      (data_arr_ram_cell_tech_type == lp_dram ||
       data_arr_ram_cell_tech_type == comm_dram)) {
    cerr << "DRAM based CAM and fully associative cache are not supported"
         << endl;
    return false;
  }

  if ((fully_assoc == true || pure_cam == true) && (is_main_mem == true)) {
    cerr << "CAM and fully associative cache cannot be as main memory" << endl;
    return false;
  }

  if ((fully_assoc || pure_cam) && SCHP < 1) {
    cerr << "CAM and fully associative must have at least 1 search port"
         << endl;
    return false;
  }

  if (RWP == 0 && ERP == 0 && SCHP > 0 && ((fully_assoc || pure_cam))) {
    ERP = SCHP;
  }

  //    if ((!(fully_assoc || pure_cam)) && SCHP>=1)
  //    {
  //	  cerr << "None CAM and fully associative cannot have search ports" <<
  // endl; 	  return false;
  //    }

  if (assoc == 0) {
    A = C / B;
    // fully_assoc = true;
  }
  else {
    if (assoc == 1) {
      A = 1;
      // fully_assoc = false;
    }
    else {
      // fully_assoc = false;
      A = assoc;
      if (is_pow2(A) == false) {
        cerr << "Associativity must be a power of 2" << endl;
        return false;
      }
    }
  }

  if (C / (B * A) <= 1 && assoc != 0) {
    cerr << "Number of sets is too small: " << endl;
    cerr << " Need to either increase cache size, or decrease associativity or "
            "block size"
         << endl;
    cerr << " (or use fully associative cache)" << endl;
    return false;
  }

  block_sz = B;

  /*dt: testing sequential access mode*/
  if (seq_access) {
    tag_assoc = A;
    data_assoc = 1;
    is_seq_acc = true;
  }
  else {
    tag_assoc = A;
    data_assoc = A;
    is_seq_acc = false;
  }

  if (assoc == 0) {
    data_assoc = 1;
  }
  num_rw_ports = RWP;
  num_rd_ports = ERP;
  num_wr_ports = EWP;
  num_se_rd_ports = NSER;
  if (!(fully_assoc || pure_cam))
    num_search_ports = 0;
  nsets = C / (B * A);

  if (temp < 300 || temp > 400 || temp % 10 != 0) {
    cerr
        << temp
        << " Temperature must be between 300 and 400 Kelvin and multiple of 10."
        << endl;
    return false;
  }

  if (nsets < 1) {
    cerr << "Less than one set..." << endl;
    return false;
  }

  //  power_gating = (array_power_gated
  //				  || bitline_floating
  //				  || wl_power_gated
  //				  || cl_power_gated
  //				  || interconect_power_gated)?true:false;

  if (power_gating) {
    array_power_gated = true;
    bitline_floating = true;
    wl_power_gated = true;
    cl_power_gated = true;
    interconect_power_gated = true;
  }
  else {
    array_power_gated = false;
    bitline_floating = false;
    wl_power_gated = false;
    cl_power_gated = false;
    interconect_power_gated = false;
  }

  //  if (power_gating && (!dvs_voltage.empty()))
  //  {
  //    cerr << "Power gating and DVS cannot be active simultaneously, please
  //    model them in two runs.\n\n"; return false;
  //  }

  if (power_gating && (pure_cam || fully_assoc)) {
    cerr << "Power gating in CAM is not supported yet.\n\n" << endl;
    return false;
  }

  if (power_gating && (is_main_mem || data_arr_ram_cell_tech_type == lp_dram ||
                       data_arr_ram_cell_tech_type == comm_dram ||
                       tag_arr_ram_cell_tech_type == lp_dram ||
                       tag_arr_ram_cell_tech_type == comm_dram ||
                       data_arr_peri_global_tech_type == lp_dram ||
                       data_arr_peri_global_tech_type == comm_dram ||
                       tag_arr_peri_global_tech_type == lp_dram ||
                       tag_arr_peri_global_tech_type == comm_dram)) {
    cerr << "Power gating in DRAM is not supported. \n\n" << endl;
    return false;
  }

  if (long_channel_device &&
      (is_main_mem || data_arr_ram_cell_tech_type == lp_dram ||
       data_arr_ram_cell_tech_type == comm_dram ||
       tag_arr_ram_cell_tech_type == lp_dram ||
       tag_arr_ram_cell_tech_type == comm_dram ||
       data_arr_peri_global_tech_type == lp_dram ||
       data_arr_peri_global_tech_type == comm_dram ||
       tag_arr_peri_global_tech_type == lp_dram ||
       tag_arr_peri_global_tech_type == comm_dram)) {
    cerr << "Long Channel Device in DRAM is not supported. \n\n" << endl;
    return false;
  }

  if ((!dvs_voltage.empty()) &&
      (is_main_mem || data_arr_ram_cell_tech_type == lp_dram ||
       data_arr_ram_cell_tech_type == comm_dram ||
       tag_arr_ram_cell_tech_type == lp_dram ||
       tag_arr_ram_cell_tech_type == comm_dram ||
       data_arr_peri_global_tech_type == lp_dram ||
       data_arr_peri_global_tech_type == comm_dram ||
       tag_arr_peri_global_tech_type == lp_dram ||
       tag_arr_peri_global_tech_type == comm_dram)) {
    cerr << "DVS in DRAM is not supported. \n\n" << endl;
    return false;
  }

  //  if (power_gating && (specific_hp_vdd
  //		  	  	  	  || specific_lstp_vdd
  //		  	  	  	  || specific_lop_vdd))
  //    {
  //      cerr << "Default Vdd is recommended when enabling power gating.\n\n"<<
  //      endl; return false;
  //    }

  if ((!dvs_voltage.empty()) &&
      ((data_arr_ram_cell_tech_type != data_arr_peri_global_tech_type) ||
       (tag_arr_peri_global_tech_type != tag_arr_ram_cell_tech_type) ||
       (data_arr_ram_cell_tech_type != tag_arr_ram_cell_tech_type))) {
    cerr << "Same device types is recommended for tag/data/cell/peripheral for "
            "DVS. Same DVS voltage will be applied to different device "
            "types\n\n";
    return false;
  }

  return true;
}

void output_data_csv(const uca_org_t &fin_res) {
  // TODO: the csv output should remain
  fstream file("out.csv", ios::in);
  bool print_index = file.fail();
  file.close();

  file.open("out.csv", ios::out | ios::app);
  if (file.fail() == true) {
    cerr << "File out.csv could not be opened successfully" << endl;
  }
  else {
    if (print_index == true) {
      file << "Tech node (nm), ";
      file << "Capacity (bytes), ";
      file << "Number of banks, ";
      file << "Associativity, ";
      file << "Output width (bits), ";
      file << "Access time (ns), ";
      file << "Random cycle time (ns), ";
      //      file << "Multisubbank interleave cycle time (ns), ";

      //      file << "Delay request network (ns), ";
      //      file << "Delay inside mat (ns), ";
      //      file << "Delay reply network (ns), ";
      //      file << "Tag array access time (ns), ";
      //      file << "Data array access time (ns), ";
      //      file << "Refresh period (microsec), ";
      //      file << "DRAM array availability (%), ";
      file << "Dynamic search energy (nJ), ";
      file << "Dynamic read energy (nJ), ";
      file << "Dynamic write energy (nJ), ";
      //      file << "Tag Dynamic read energy (nJ), ";
      //      file << "Data Dynamic read energy (nJ), ";
      //      file << "Dynamic read power (mW), ";
      file << "Standby leakage per bank(mW), ";
      //      file << "Leakage per bank with leak power management (mW), ";
      //      file << "Leakage per bank with leak power management (mW), ";
      //      file << "Refresh power as percentage of standby leakage, ";
      file << "Area (mm2), ";
      file << "Ndwl, ";
      file << "Ndbl, ";
      file << "Nspd, ";
      file << "Ndcm, ";
      file << "Ndsam_level_1, ";
      file << "Ndsam_level_2, ";
      file << "Data arrary area efficiency %, ";
      file << "Ntwl, ";
      file << "Ntbl, ";
      file << "Ntspd, ";
      file << "Ntcm, ";
      file << "Ntsam_level_1, ";
      file << "Ntsam_level_2, ";
      file << "Tag arrary area efficiency %, ";

      //      file << "Resistance per unit micron (ohm-micron), ";
      //      file << "Capacitance per unit micron (fF per micron), ";
      //      file << "Unit-length wire delay (ps), ";
      //      file << "FO4 delay (ps), ";
      //      file << "delay route to bank (including crossb delay) (ps), ";
      //      file << "Crossbar delay (ps), ";
      //      file << "Dyn read energy per access from closed page (nJ), ";
      //      file << "Dyn read energy per access from open page (nJ), ";
      //      file << "Leak power of an subbank with page closed (mW), ";
      //      file << "Leak power of a subbank with page  open (mW), ";
      //      file << "Leak power of request and reply networks (mW), ";
      //      file << "Number of subbanks, ";
      //      file << "Page size in bits, ";
      //      file << "Activate power, ";
      //      file << "Read power, ";
      //      file << "Write power, ";
      //      file << "Precharge power, ";
      //      file << "tRCD, ";
      //      file << "CAS latency, ";
      //      file << "Precharge delay, ";
      //      file << "Perc dyn energy bitlines, ";
      //      file << "perc dyn energy wordlines, ";
      //      file << "perc dyn energy outside mat, ";
      //      file << "Area opt (perc), ";
      //      file << "Delay opt (perc), ";
      //      file << "Repeater opt (perc), ";
      //      file << "Aspect ratio";
      file << endl;
    }
    file << g_ip->F_sz_nm << ", ";
    file << g_ip->cache_sz << ", ";
    file << g_ip->nbanks << ", ";
    file << g_ip->tag_assoc << ", ";
    file << g_ip->out_w << ", ";
    file << fin_res.access_time * 1e+9 << ", ";
    file << fin_res.cycle_time * 1e+9 << ", ";
    //    file << fin_res.data_array2->multisubbank_interleave_cycle_time*1e+9
    //    << ", "; file << fin_res.data_array2->delay_request_network*1e+9 << ",
    //    "; file << fin_res.data_array2->delay_inside_mat*1e+9 <<  ", "; file
    //    << fin_res.data_array2.delay_reply_network*1e+9 << ", ";

    //    if (!(g_ip->fully_assoc || g_ip->pure_cam || g_ip->pure_ram))
    //        {
    //    	  file << fin_res.tag_array2->access_time*1e+9 << ", ";
    //        }
    //    else
    //    {
    //    	file << 0 << ", ";
    //    }
    //    file << fin_res.data_array2->access_time*1e+9 << ", ";
    //    file << fin_res.data_array2->dram_refresh_period*1e+6 << ", ";
    //    file << fin_res.data_array2->dram_array_availability <<  ", ";
    if (g_ip->fully_assoc || g_ip->pure_cam) {
      file << fin_res.power.searchOp.dynamic * 1e+9 << ", ";
    }
    else {
      file << "N/A"
           << ", ";
    }
    file << fin_res.power.readOp.dynamic * 1e+9 << ", ";
    file << fin_res.power.writeOp.dynamic * 1e+9 << ", ";
    //    if (!(g_ip->fully_assoc || g_ip->pure_cam || g_ip->pure_ram))
    //        {
    //        	file << fin_res.tag_array2->power.readOp.dynamic*1e+9 << ", ";
    //        }
    //        	else
    //        {
    //        		file << "NA" << ", ";
    //        }
    //    file << fin_res.data_array2->power.readOp.dynamic*1e+9 << ", ";
    //    if (g_ip->fully_assoc || g_ip->pure_cam)
    //        {
    //    	    file <<
    //    fin_res.power.searchOp.dynamic*1000/fin_res.cycle_time << ", ";
    //        }
    //        	else
    //        {
    //        	file << fin_res.power.readOp.dynamic*1000/fin_res.cycle_time <<
    //        ", ";
    //        }

    file << (fin_res.power.readOp.leakage + fin_res.power.readOp.gate_leakage) *
                1000
         << ", ";
    //    file << fin_res.leak_power_with_sleep_transistors_in_mats*1000 << ",
    //    "; file << fin_res.data_array.refresh_power /
    //    fin_res.data_array.total_power.readOp.leakage << ", ";
    file << fin_res.area * 1e-6 << ", ";

    file << fin_res.data_array2->Ndwl << ", ";
    file << fin_res.data_array2->Ndbl << ", ";
    file << fin_res.data_array2->Nspd << ", ";
    file << fin_res.data_array2->deg_bl_muxing << ", ";
    file << fin_res.data_array2->Ndsam_lev_1 << ", ";
    file << fin_res.data_array2->Ndsam_lev_2 << ", ";
    file << fin_res.data_array2->area_efficiency << ", ";
    if (!(g_ip->fully_assoc || g_ip->pure_cam || g_ip->pure_ram)) {
      file << fin_res.tag_array2->Ndwl << ", ";
      file << fin_res.tag_array2->Ndbl << ", ";
      file << fin_res.tag_array2->Nspd << ", ";
      file << fin_res.tag_array2->deg_bl_muxing << ", ";
      file << fin_res.tag_array2->Ndsam_lev_1 << ", ";
      file << fin_res.tag_array2->Ndsam_lev_2 << ", ";
      file << fin_res.tag_array2->area_efficiency << ", ";
    }
    else {
      file << "N/A"
           << ", ";
      file << "N/A"
           << ", ";
      file << "N/A"
           << ", ";
      file << "N/A"
           << ", ";
      file << "N/A"
           << ", ";
      file << "N/A"
           << ", ";
      file << "N/A"
           << ", ";
    }

    //    file << g_tp.wire_inside_mat.R_per_um << ", ";
    //    file << g_tp.wire_inside_mat.C_per_um / 1e-15 << ", ";
    //    file << g_tp.unit_len_wire_del / 1e-12 << ", ";
    //    file << g_tp.FO4 / 1e-12 << ", ";
    //    file << fin_res.data_array.delay_route_to_bank / 1e-9 << ", ";
    //    file << fin_res.data_array.delay_crossbar / 1e-9 << ", ";
    //    file << fin_res.data_array.dyn_read_energy_from_closed_page / 1e-9 <<
    //    ", "; file << fin_res.data_array.dyn_read_energy_from_open_page / 1e-9
    //    << ", "; file << fin_res.data_array.leak_power_subbank_closed_page /
    //    1e-3 << ", "; file << fin_res.data_array.leak_power_subbank_open_page
    //    / 1e-3 << ", "; file <<
    //    fin_res.data_array.leak_power_request_and_reply_networks / 1e-3 << ",
    //    "; file << fin_res.data_array.number_subbanks << ", " ; file <<
    //    fin_res.data_array.page_size_in_bits << ", " ; file <<
    //    fin_res.data_array.activate_energy * 1e9 << ", " ; file <<
    //    fin_res.data_array.read_energy * 1e9 << ", " ; file <<
    //    fin_res.data_array.write_energy * 1e9 << ", " ; file <<
    //    fin_res.data_array.precharge_energy * 1e9 << ", " ; file <<
    //    fin_res.data_array.trcd * 1e9 << ", " ; file <<
    //    fin_res.data_array.cas_latency * 1e9 << ", " ; file <<
    //    fin_res.data_array.precharge_delay * 1e9 << ", " ; file <<
    //    fin_res.data_array.all_banks_height /
    //    fin_res.data_array.all_banks_width;
    file << endl;
  }
  file.close();
}

void output_UCA(uca_org_t *fr) {
  double long_channel_leakage_reduction =
      0.1 +
      0.9 * (0.8 * fr->data_array2->long_channel_leakage_reduction_memcell +
             0.2 * fr->data_array2
                       ->long_channel_leakage_reduction_periperal);  // TODO
  double areaoverhead, overhead_data, overhead_tag;
  double wakeup_E, wakeup_T, wakeup_E_data, wakeup_T_data = 0., wakeup_E_tag,
                                            wakeup_T_tag = 0.;
  int dvs_levels = g_ip->dvs_voltage.size();
  int i;
  bool dvs = !g_ip->dvs_voltage.empty();
  //    if (NUCA)
  if (0) {
    cout << "\n\n Detailed Bank Stats:\n";
    cout << "    Bank Size (bytes): %d\n" << (int)(g_ip->cache_sz);
  }
  else {
    if (g_ip->data_arr_ram_cell_tech_type == 3) {
      cout << "\n---------- CACTI-P,  with new features: " << VER_COMMENT_CACTI
           << " of " << VER_UPDATE_CACTI << "), Uniform Cache Access "
           << "Logic Process Based DRAM Model ----------\n";
    }
    else if (g_ip->data_arr_ram_cell_tech_type == 4) {
      cout << "\n---------- CACTI-P,  with new features: " << VER_COMMENT_CACTI
           << " of " << VER_UPDATE_CACTI << "), Uniform"
           << "Cache Access Commodity DRAM Model ----------\n";
    }
    else {
      cout << "\n---------- CACTI-P,  with new features: " << VER_COMMENT_CACTI
           << " of " << VER_UPDATE_CACTI
           << "), Uniform Cache Access "
              "SRAM Model ----------\n";
    }
    //    (version "<< VER_MAJOR_CACTI <<"."<< VER_MINOR_CACTI<<
    //    VER_postfix_CACTI<<"; (version "<< VER_MAJOR_CACTI <<"."<<
    //    VER_MINOR_CACTI<<"."VER_COMMENT_CACTI << VER_postfix_CACTI
    //    								<< " of
    //    "
    //    << VER_UPDATE_CACTI << ")
    cout << "\nCache Parameters:\n";
    cout << "    Total cache size (bytes): " << (int)(g_ip->cache_sz) << endl;
  }

  cout << "    Number of banks: " << (int)g_ip->nbanks << endl;
  if (g_ip->fully_assoc || g_ip->pure_cam)
    cout << "    Associativity: fully associative\n";
  else {
    if (g_ip->tag_assoc == 1)
      cout << "    Associativity: direct mapped\n";
    else
      cout << "    Associativity: " << g_ip->tag_assoc << endl;
  }

  cout << "    Block size (bytes): " << g_ip->line_sz << endl;
  cout << "    Read/write Ports: " << g_ip->num_rw_ports << endl;
  cout << "    Read ports: " << g_ip->num_rd_ports << endl;
  cout << "    Write ports: " << g_ip->num_wr_ports << endl;
  if (g_ip->fully_assoc || g_ip->pure_cam)
    cout << "    search ports: " << g_ip->num_search_ports << endl;
  cout << "    Technology size (nm): " << g_ip->F_sz_nm << endl << endl;

  cout << "    Access time (ns): " << fr->access_time * 1e9;
  if (dvs) {
    cout << " (@DVS_Level0); ";
    for (i = 0; i < dvs_levels; i++)
      cout << fr->uca_q[i]->access_time * 1e9 << " (@DVS_Level" << i + 1
           << "_Vdd=" << g_ip->dvs_voltage[i] << "); ";
  }
  cout << endl;

  cout << "    Cycle time (ns):  " << fr->cycle_time * 1e9;
  if (dvs) {
    cout << " (@DVS_Level0); ";
    for (i = 0; i < dvs_levels; i++)
      cout << fr->uca_q[i]->cycle_time * 1e9 << " (@DVS_Level" << i + 1
           << "_Vdd=" << g_ip->dvs_voltage[i] << "); ";
  }
  cout << endl;
  if (g_ip->data_arr_ram_cell_tech_type >= 4) {
    cout << "    Precharge Delay (ns): "
         << fr->data_array2->precharge_delay * 1e9 << endl;
    cout << "    Activate Energy (nJ): "
         << fr->data_array2->activate_energy * 1e9 << endl;
    cout << "    Read Energy (nJ): " << fr->data_array2->read_energy * 1e9
         << endl;
    cout << "    Write Energy (nJ): " << fr->data_array2->write_energy * 1e9
         << endl;
    cout << "    Precharge Energy (nJ): "
         << fr->data_array2->precharge_energy * 1e9 << endl;
    cout << "    Leakage Power Closed Page (mW): "
         << fr->data_array2->leak_power_subbank_closed_page * 1e3 << endl;
    cout << "    Leakage Power Open Page (mW): "
         << fr->data_array2->leak_power_subbank_open_page * 1e3 << endl;
    cout << "    Leakage Power I/O (mW): "
         << fr->data_array2->leak_power_request_and_reply_networks * 1e3
         << endl;
    cout << "    Refresh power (mW): " << fr->data_array2->refresh_power * 1e3
         << endl;
  }
  else {
    if ((g_ip->fully_assoc || g_ip->pure_cam)) {
      cout << "    Total dynamic associative search energy per access (nJ): "
           << fr->power.searchOp.dynamic * 1e9;
      if (dvs) {
        cout << " (@DVS_Level0); ";
        for (i = 0; i < dvs_levels; i++)
          cout << fr->uca_q[i]->power.searchOp.dynamic * 1e9 << " (@DVS_Level"
               << i + 1 << "_Vdd=" << g_ip->dvs_voltage[i] << "); ";
      }
      cout << endl;
      //		  cout << "    Total dynamic read energy per access
      //(nJ): " << 		  fr->power.readOp.dynamic*1e9 << endl;
      //		  cout << "    Total dynamic write energy per access
      //(nJ): " << 		  fr->power.writeOp.dynamic*1e9 << endl;
    }
    //	  else
    //	  {
    cout << "    Total dynamic read energy per access (nJ): "
         << fr->power.readOp.dynamic * 1e9;
    if (dvs) {
      cout << " (@DVS_Level0); ";
      for (i = 0; i < dvs_levels; i++)
        cout << fr->uca_q[i]->power.readOp.dynamic * 1e9 << " (@DVS_Level"
             << i + 1 << "_Vdd=" << g_ip->dvs_voltage[i] << "); ";
    }
    cout << endl;
    cout << "    Total dynamic write energy per access (nJ): "
         << fr->power.writeOp.dynamic * 1e9;
    if (dvs) {
      cout << " (@DVS_Level0); ";
      for (i = 0; i < dvs_levels; i++)
        cout << fr->uca_q[i]->power.writeOp.dynamic * 1e9 << " (@DVS_Level"
             << i + 1 << "_Vdd=" << g_ip->dvs_voltage[i] << "); ";
    }
    cout << endl;
    //	  }
    if (g_ip->power_gating) {
      cout << "    Total leakage power of a bank, with power-gating ";
      if (!g_ip->user_defined_vcc_underflow) {
        cout << "(state retained)";
      }
      else {
        cout << "(non state retained)";
      }

      cout << ", including its network outside"  // power gated with retaining
                                                 // memory content
              " (mW): "
           << (g_ip->long_channel_device
                   ? fr->power.readOp.power_gated_leakage *
                         long_channel_leakage_reduction
                   : fr->power.readOp.power_gated_leakage) *
                  1e3
           << endl;
    }
    cout << "    Total leakage power of a bank without power gating, including "
            "its network outside"
            " (mW): "
         << (g_ip->long_channel_device
                 ? fr->power.readOp.leakage * long_channel_leakage_reduction
                 : fr->power.readOp.leakage) *
                1e3;

    if (dvs) {
      cout << " (@DVS_Level0); ";
      for (i = 0; i < dvs_levels; i++)
        cout << (g_ip->long_channel_device
                     ? fr->uca_q[i]->power.readOp.leakage *
                           long_channel_leakage_reduction
                     : fr->uca_q[i]->power.readOp.leakage) *
                    1e3
             << " (@DVS_Level" << i + 1 << "_Vdd=" << g_ip->dvs_voltage[i]
             << "); ";
    }
    cout << endl;
  }

  if (g_ip->data_arr_ram_cell_tech_type == 3 ||
      g_ip->data_arr_ram_cell_tech_type == 4) {
  }
  cout << "    Cache height x width (mm): " << fr->cache_ht * 1e-3 << " x "
       << fr->cache_len * 1e-3 << endl;
  cout << endl;

  if (g_ip->power_gating) {
    /* Energy/Power stats */
    cout << "    Power-gating results (The virtual power supply for gated "
            "circuit can only retain the state of idle circuit, not for "
            "operating the circuit):"
         << endl;
    /* Data array power-gating stats */
    if (g_ip->user_defined_vcc_underflow) {
      cout << "    Warning: user defined power gating voltage is too low to "
              "retain state; Please understand the implications of deep sleep "
              "state on non state retaining and cold start effects when waking "
              "up the structure."
           << endl;
    }
    else {
      cout << "    Power-gating results when retaining state" << endl;
    }
    areaoverhead =
        (fr->cache_ht * fr->cache_len / fr->uca_pg_reference->cache_ht /
             fr->uca_pg_reference->cache_len -
         1) *
        100;  //%
    cout << "    \tPower gating circuits (sleep transistors) induced area "
            "overhead: "
         << areaoverhead << " % " << endl;
    wakeup_E = wakeup_E_data = fr->data_array2->sram_sleep_wakeup_energy +
                               fr->data_array2->wl_sleep_wakeup_energy +
                               fr->data_array2->bl_floating_wakeup_energy;
    wakeup_T = wakeup_T_data =
        MAX(fr->data_array2->sram_sleep_wakeup_latency,
            MAX(fr->data_array2->wl_sleep_wakeup_latency,
                fr->data_array2->bl_floating_wakeup_latency));
    if ((!(g_ip->pure_ram || g_ip->pure_cam || g_ip->fully_assoc)) &&
        !g_ip->is_main_mem) {
      wakeup_E_tag = fr->tag_array2->sram_sleep_wakeup_energy +
                     fr->tag_array2->wl_sleep_wakeup_energy +
                     fr->tag_array2->bl_floating_wakeup_energy;
      wakeup_T_tag = MAX(fr->tag_array2->sram_sleep_wakeup_latency,
                         MAX(fr->tag_array2->wl_sleep_wakeup_latency,
                             fr->tag_array2->bl_floating_wakeup_latency));

      wakeup_E += wakeup_E_tag;
      wakeup_T = MAX(wakeup_T_tag, wakeup_T_data);
    }
    cout << "    \tPower gating Wakeup Latency (ns): " << wakeup_T * 1e9
         << endl;
    cout << "    \tPower gating Wakeup Energy (nJ): " << wakeup_E * 1e9 << endl;
  }
  cout << endl << endl;
  cout << "    Best Ndwl : " << fr->data_array2->Ndwl << endl;
  cout << "    Best Ndbl : " << fr->data_array2->Ndbl << endl;
  cout << "    Best Nspd : " << fr->data_array2->Nspd << endl;
  cout << "    Best Ndcm : " << fr->data_array2->deg_bl_muxing << endl;
  cout << "    Best Ndsam L1 : " << fr->data_array2->Ndsam_lev_1 << endl;
  cout << "    Best Ndsam L2 : " << fr->data_array2->Ndsam_lev_2 << endl
       << endl;

  if ((!(g_ip->pure_ram || g_ip->pure_cam || g_ip->fully_assoc)) &&
      !g_ip->is_main_mem) {
    cout << "    Best Ntwl : " << fr->tag_array2->Ndwl << endl;
    cout << "    Best Ntbl : " << fr->tag_array2->Ndbl << endl;
    cout << "    Best Ntspd : " << fr->tag_array2->Nspd << endl;
    cout << "    Best Ntcm : " << fr->tag_array2->deg_bl_muxing << endl;
    cout << "    Best Ntsam L1 : " << fr->tag_array2->Ndsam_lev_1 << endl;
    cout << "    Best Ntsam L2 : " << fr->tag_array2->Ndsam_lev_2 << endl;
  }

  switch (fr->data_array2->wt) {
    case (0):
      cout
          << "    Data array, H-tree wire type: Delay optimized global wires\n";
      break;
    case (1):
      cout << "    Data array, H-tree wire type: Global wires with 5\% delay "
              "penalty\n";
      break;
    case (2):
      cout << "    Data array, H-tree wire type: Global wires with 10\% delay "
              "penalty\n";
      break;
    case (3):
      cout << "    Data array, H-tree wire type: Global wires with 20\% delay "
              "penalty\n";
      break;
    case (4):
      cout << "    Data array, H-tree wire type: Global wires with 30\% delay "
              "penalty\n";
      break;
    case (5):
      cout << "    Data array, wire type: Low swing wires\n";
      break;
    default:
      cout << "ERROR - Unknown wire type " << (int)fr->data_array2->wt << endl;
      exit(0);
  }

  if (!(g_ip->pure_ram || g_ip->pure_cam || g_ip->fully_assoc)) {
    switch (fr->tag_array2->wt) {
      case (0):
        cout << "    Tag array, H-tree wire type: Delay optimized global "
                "wires\n";
        break;
      case (1):
        cout << "    Tag array, H-tree wire type: Global wires with 5\% delay "
                "penalty\n";
        break;
      case (2):
        cout << "    Tag array, H-tree wire type: Global wires with 10\% delay "
                "penalty\n";
        break;
      case (3):
        cout << "    Tag array, H-tree wire type: Global wires with 20\% delay "
                "penalty\n";
        break;
      case (4):
        cout << "    Tag array, H-tree wire type: Global wires with 30\% delay "
                "penalty\n";
        break;
      case (5):
        cout << "    Tag array, wire type: Low swing wires\n";
        break;
      default:
        cout << "ERROR - Unknown wire type " << (int)fr->tag_array2->wt << endl;
        exit(-1);
    }
  }

  if (g_ip->print_detail) {
    // if(g_ip->fully_assoc) return;
    if (0) {  // detailed power-gating output
      if (g_ip->power_gating) {
        /* Energy/Power stats */
        cout << endl << endl << "Power-gating Components:" << endl << endl;
        /* Data array power-gating stats */
        areaoverhead = fr->cache_ht * fr->cache_len /
                           fr->uca_pg_reference->cache_ht /
                           fr->uca_pg_reference->cache_len -
                       1;
        cout << "    Power gating circuits (sleep transistors) induced area "
                "overhead: "
             << areaoverhead << " % " << endl;
        wakeup_E = wakeup_E_data = fr->data_array2->sram_sleep_wakeup_energy +
                                   fr->data_array2->wl_sleep_wakeup_energy +
                                   fr->data_array2->bl_floating_wakeup_energy;
        wakeup_T = wakeup_T_data =
            MAX(fr->data_array2->sram_sleep_wakeup_latency,
                MAX(fr->data_array2->wl_sleep_wakeup_latency,
                    fr->data_array2->bl_floating_wakeup_latency));
        if ((!(g_ip->pure_ram || g_ip->pure_cam || g_ip->fully_assoc)) &&
            !g_ip->is_main_mem) {
          wakeup_E_tag = fr->tag_array2->sram_sleep_wakeup_energy +
                         fr->tag_array2->wl_sleep_wakeup_energy +
                         fr->tag_array2->bl_floating_wakeup_energy;
          wakeup_T_tag = MAX(fr->tag_array2->sram_sleep_wakeup_latency,
                             MAX(fr->tag_array2->wl_sleep_wakeup_latency,
                                 fr->tag_array2->bl_floating_wakeup_latency));

          wakeup_E += wakeup_E_tag;
          wakeup_T = MAX(wakeup_T_tag, wakeup_T_data);
        }
        cout << "    Power gating Wakeup Latency (ns): " << wakeup_T * 1e9
             << endl;
        cout << "    Power gating Wakeup Energy (nJ): " << wakeup_E * 1e9
             << endl;

        // extra power gating details
        if (!(g_ip->pure_cam || g_ip->fully_assoc))
          cout << "  Data array: " << endl;
        else if (g_ip->pure_cam)
          cout << "  CAM array: " << endl;
        else
          cout << "  Fully associative cache array: " << endl;

        cout << "\t Sub-array Sleep Tx size (um) - "
             << fr->data_array2->sram_sleep_tx_width << endl;

        //    cout <<  "\t Sub-array Sleep Tx total size (um) - " <<
        //      fr->data_array2->sram_sleep_tx_width << endl;

        cout << "\t Sub-array Sleep Tx total area (mm^2) - "
             << fr->data_array2->sram_sleep_tx_area * 1e-6 << endl;

        cout << "\t Sub-array wakeup time (ns) - "
             << fr->data_array2->sram_sleep_wakeup_latency * 1e9 << endl;

        cout << "\t Sub-array Tx energy (nJ) - "
             << fr->data_array2->sram_sleep_wakeup_energy * 1e9 << endl;
        //+++++++++++++++++++++++++++++++++++++++++++++++++++++++++
        cout << endl;
        cout << "\t WL Sleep Tx size (um) - "
             << fr->data_array2->wl_sleep_tx_width << endl;

        //    cout <<  "\t WL Sleep total Tx size (um) - " <<
        //      fr->data_array2->wl_sleep_tx_width << endl;

        cout << "\t WL Sleep Tx total area (mm^2) - "
             << fr->data_array2->wl_sleep_tx_area * 1e-6 << endl;

        cout << "\t WL wakeup time (ns) - "
             << fr->data_array2->wl_sleep_wakeup_latency * 1e9 << endl;

        cout << "\t WL Tx energy (nJ) - "
             << fr->data_array2->wl_sleep_wakeup_energy * 1e9 << endl;
        //+++++++++++++++++++++++++++++++++++++++++++++++++++++++++
        cout << endl;
        cout << "\t BL floating wakeup time (ns) - "
             << fr->data_array2->bl_floating_wakeup_latency * 1e9 << endl;

        cout << "\t BL floating Tx energy (nJ) - "
             << fr->data_array2->bl_floating_wakeup_energy * 1e9 << endl;
        //+++++++++++++++++++++++++++++++++++++++++++++++++++++++++

        cout << endl;

        cout << "\t Active mats per access - "
             << fr->data_array2->num_active_mats << endl;
        cout << "\t Active subarrays per mat - "
             << fr->data_array2->num_submarray_mats << endl;
        cout << endl;
        /* Tag array area stats */
        if ((!(g_ip->pure_ram || g_ip->pure_cam || g_ip->fully_assoc)) &&
            !g_ip->is_main_mem) {
          cout << "  Tag array: " << endl;
          cout << "\t Sub-array Sleep Tx size (um) - "
               << fr->tag_array2->sram_sleep_tx_width << endl;

          //    cout <<  "\t Sub-array Sleep Tx total size (um) - " <<
          //      fr->tag_array2->sram_sleep_tx_width << endl;

          cout << "\t Sub-array Sleep Tx total area (mm^2) - "
               << fr->tag_array2->sram_sleep_tx_area * 1e-6 << endl;

          cout << "\t Sub-array wakeup time (ns) - "
               << fr->tag_array2->sram_sleep_wakeup_latency * 1e9 << endl;

          cout << "\t Sub-array Tx energy (nJ) - "
               << fr->tag_array2->sram_sleep_wakeup_energy * 1e9 << endl;
          //+++++++++++++++++++++++++++++++++++++++++++++++++++++++++
          cout << endl;
          cout << "\t WL Sleep Tx size (um) - "
               << fr->tag_array2->wl_sleep_tx_width << endl;

          //    cout <<  "\t WL Sleep total Tx size (um) - " <<
          //      fr->tag_array2->wl_sleep_tx_width << endl;

          cout << "\t WL Sleep Tx total area (mm^2) - "
               << fr->tag_array2->wl_sleep_tx_area * 1e-6 << endl;

          cout << "\t WL wakeup time (ns) - "
               << fr->tag_array2->wl_sleep_wakeup_latency * 1e9 << endl;

          cout << "\t WL Tx energy (nJ) - "
               << fr->tag_array2->wl_sleep_wakeup_energy * 1e9 << endl;
          //+++++++++++++++++++++++++++++++++++++++++++++++++++++++++
          cout << endl;
          cout << "\t BL floating wakeup time (ns) - "
               << fr->tag_array2->bl_floating_wakeup_latency * 1e9 << endl;

          cout << "\t BL floating Tx energy (nJ) - "
               << fr->tag_array2->bl_floating_wakeup_energy * 1e9 << endl;
          //+++++++++++++++++++++++++++++++++++++++++++++++++++++++++
          cout << endl;

          cout << "\t Active mats per access - "
               << fr->tag_array2->num_active_mats << endl;
          cout << "\t Active subarrays per mat - "
               << fr->tag_array2->num_submarray_mats << endl;
          cout << endl;
        }
      }
    }
    /* Delay stats */
    /* data array stats */
    cout << endl << "Time Components:" << endl << endl;

    cout << "  Data side (with Output driver) (ns): "
         << fr->data_array2->access_time / 1e-9;
    if (dvs) {
      cout << " (@DVS_Level0); ";
      for (i = 0; i < dvs_levels; i++)
        cout << fr->uca_q[i]->data_array2->access_time / 1e-9 << " (@DVS_Level"
             << i + 1 << "_Vdd=" << g_ip->dvs_voltage[i] << "); ";
    }
    cout << endl;

    cout << "\tH-tree delay outside banks (ns): "
         << fr->data_array2->delay_route_to_bank * 1e9;
    if (dvs) {
      cout << " (@DVS_Level0); ";
      for (i = 0; i < dvs_levels; i++)
        cout << fr->uca_q[i]->data_array2->delay_route_to_bank * 1e9
             << " (@DVS_Level" << i + 1 << "_Vdd=" << g_ip->dvs_voltage[i]
             << "); ";
    }
    cout << endl;

    cout << "\tH-tree input delay (inside a bank) (ns): "
         << fr->data_array2->delay_input_htree * 1e9;
    if (dvs) {
      cout << " (@DVS_Level0); ";
      for (i = 0; i < dvs_levels; i++)
        cout << fr->uca_q[i]->data_array2->delay_input_htree * 1e9
             << " (@DVS_Level" << i + 1 << "_Vdd=" << g_ip->dvs_voltage[i]
             << "); ";
    }
    cout << endl;

    if (!(g_ip->pure_cam || g_ip->fully_assoc)) {
      cout << "\tDecoder + wordline delay (ns): "
           << fr->data_array2->delay_row_predecode_driver_and_block * 1e9 +
                  fr->data_array2->delay_row_decoder * 1e9;
      if (dvs) {
        cout << " (@DVS_Level0); ";
        for (i = 0; i < dvs_levels; i++)
          cout << fr->uca_q[i]->data_array2
                              ->delay_row_predecode_driver_and_block *
                          1e9 +
                      fr->uca_q[i]->data_array2->delay_row_decoder * 1e9
               << " (@DVS_Level" << i + 1 << "_Vdd=" << g_ip->dvs_voltage[i]
               << "); ";
      }
      cout << endl;
    }
    else {
      cout << "\tCAM search delay (ns): "
           << fr->data_array2->delay_matchlines * 1e9;
      if (dvs) {
        cout << " (@DVS_Level0); ";
        for (i = 0; i < dvs_levels; i++)
          cout << fr->uca_q[i]->data_array2->delay_matchlines * 1e9
               << " (@DVS_Level" << i + 1 << "_Vdd=" << g_ip->dvs_voltage[i]
               << "); ";
      }
      cout << endl;
    }

    cout << "\tBitline delay (ns): " << fr->data_array2->delay_bitlines / 1e-9;
    if (dvs) {
      cout << " (@DVS_Level0); ";
      for (i = 0; i < dvs_levels; i++)
        cout << fr->uca_q[i]->data_array2->delay_bitlines / 1e-9
             << " (@DVS_Level" << i + 1 << "_Vdd=" << g_ip->dvs_voltage[i]
             << "); ";
    }
    cout << endl;

    cout << "\tSense Amplifier delay (ns): "
         << fr->data_array2->delay_sense_amp * 1e9;
    if (dvs) {
      cout << " (@DVS_Level0); ";
      for (i = 0; i < dvs_levels; i++)
        cout << fr->uca_q[i]->data_array2->delay_sense_amp * 1e9
             << " (@DVS_Level" << i + 1 << "_Vdd=" << g_ip->dvs_voltage[i]
             << "); ";
    }
    cout << endl;

    cout << "\tH-tree output delay (inside a bank) (ns): "
         << fr->data_array2->delay_subarray_output_driver * 1e9 +
                fr->data_array2->delay_dout_htree * 1e9;
    if (dvs) {
      cout << " (@DVS_Level0); ";
      for (i = 0; i < dvs_levels; i++)
        cout << fr->uca_q[i]->data_array2->delay_subarray_output_driver * 1e9 +
                    fr->uca_q[i]->data_array2->delay_dout_htree * 1e9
             << " (@DVS_Level" << i + 1 << "_Vdd=" << g_ip->dvs_voltage[i]
             << "); ";
    }
    cout << endl;
    if (g_ip->power_gating) {
      cout << "\tPower gating wakeup time (ns) - " << wakeup_T_data * 1e9
           << endl;
    }

    if ((!(g_ip->pure_ram || g_ip->pure_cam || g_ip->fully_assoc)) &&
        !g_ip->is_main_mem) {
      /* tag array stats */
      cout << endl
           << "  Tag side (with Output driver) (ns): "
           << fr->tag_array2->access_time / 1e-9;
      if (dvs) {
        cout << " (@DVS_Level0); ";
        for (i = 0; i < dvs_levels; i++)
          cout << fr->uca_q[i]->tag_array2->access_time / 1e-9 << " (@DVS_Level"
               << i + 1 << "_Vdd=" << g_ip->dvs_voltage[i] << "); ";
      }
      cout << endl;
      cout << "\tH-tree delay outside banks (ns): "
           << fr->tag_array2->delay_route_to_bank * 1e9;
      if (dvs) {
        cout << " (@DVS_Level0); ";
        for (i = 0; i < dvs_levels; i++)
          cout << fr->uca_q[i]->tag_array2->delay_route_to_bank * 1e9
               << " (@DVS_Level" << i + 1 << "_Vdd=" << g_ip->dvs_voltage[i]
               << "); ";
      }
      cout << endl;

      cout << "\tH-tree input delay (inside a bank) (ns): "
           << fr->tag_array2->delay_input_htree * 1e9;
      if (dvs) {
        cout << " (@DVS_Level0); ";
        for (i = 0; i < dvs_levels; i++)
          cout << fr->uca_q[i]->tag_array2->delay_input_htree * 1e9
               << " (@DVS_Level" << i + 1 << "_Vdd=" << g_ip->dvs_voltage[i]
               << "); ";
      }
      cout << endl;

      cout << "\tDecoder + wordline delay (ns): "
           << fr->tag_array2->delay_row_predecode_driver_and_block * 1e9 +
                  fr->tag_array2->delay_row_decoder * 1e9;
      if (dvs) {
        cout << " (@DVS_Level0); ";
        for (i = 0; i < dvs_levels; i++)
          cout << fr->uca_q[i]->tag_array2
                              ->delay_row_predecode_driver_and_block *
                          1e9 +
                      fr->uca_q[i]->tag_array2->delay_row_decoder * 1e9
               << " (@DVS_Level" << i + 1 << "_Vdd=" << g_ip->dvs_voltage[i]
               << "); ";
      }
      cout << endl;

      cout << "\tBitline delay (ns): " << fr->tag_array2->delay_bitlines / 1e-9;
      if (dvs) {
        cout << " (@DVS_Level0); ";
        for (i = 0; i < dvs_levels; i++)
          cout << fr->uca_q[i]->tag_array2->delay_bitlines * 1e9
               << " (@DVS_Level" << i + 1 << "_Vdd=" << g_ip->dvs_voltage[i]
               << "); ";
      }
      cout << endl;

      cout << "\tSense Amplifier delay (ns): "
           << fr->tag_array2->delay_sense_amp * 1e9;
      if (dvs) {
        cout << " (@DVS_Level0); ";
        for (i = 0; i < dvs_levels; i++)
          cout << fr->uca_q[i]->tag_array2->delay_sense_amp * 1e9
               << " (@DVS_Level" << i + 1 << "_Vdd=" << g_ip->dvs_voltage[i]
               << "); ";
      }
      cout << endl;

      cout << "\tComparator delay (ns): "
           << fr->tag_array2->delay_comparator * 1e9;
      if (dvs) {
        cout << " (@DVS_Level0); ";
        for (i = 0; i < dvs_levels; i++)
          cout << fr->uca_q[i]->tag_array2->delay_comparator * 1e9
               << " (@DVS_Level" << i + 1 << "_Vdd=" << g_ip->dvs_voltage[i]
               << "); ";
      }
      cout << endl;

      cout << "\tH-tree output delay (inside a bank) (ns): "
           << fr->tag_array2->delay_subarray_output_driver * 1e9 +
                  fr->tag_array2->delay_dout_htree * 1e9;
      if (dvs) {
        cout << " (@DVS_Level0); ";
        for (i = 0; i < dvs_levels; i++)
          cout << fr->uca_q[i]->tag_array2->delay_subarray_output_driver * 1e9 +
                      fr->uca_q[i]->tag_array2->delay_dout_htree * 1e9
               << " (@DVS_Level" << i + 1 << "_Vdd=" << g_ip->dvs_voltage[i]
               << "); ";
      }
      cout << endl;
      if (g_ip->power_gating) {
        cout << "\tPower gating wakeup time (ns) - " << wakeup_T_tag * 1e9
             << endl;
      }
    }

    /* Energy/Power stats */
    cout << endl << endl << "Power Components:" << endl << endl;

    if (!(g_ip->pure_cam || g_ip->fully_assoc)) {
      cout << "  Data array: Total dynamic read energy/access  (nJ): "
           << fr->data_array2->power.readOp.dynamic * 1e9;
      if (dvs) {
        cout << " (@DVS_Level0); ";
        for (i = 0; i < dvs_levels; i++)
          cout << fr->uca_q[i]->data_array2->power.readOp.dynamic * 1e9
               << " (@DVS_Level" << i + 1 << "_Vdd=" << g_ip->dvs_voltage[i]
               << "); ";
      }
      cout << endl;

      if (g_ip->power_gating) {
        cout << "\tTotal leakage power of a bank, power gated ";

        if (!g_ip->user_defined_vcc_underflow) {
          cout << "with ";
        }
        else {
          cout << "without ";
        }
        cout << "retaining memory content, including its network outside (mW): "
             << (g_ip->long_channel_device
                     ? fr->data_array2->power.readOp.power_gated_leakage *
                           long_channel_leakage_reduction
                     : fr->data_array2->power.readOp.power_gated_leakage) *
                    1e3
             << endl;
      }
      //    	else
      //    	{
      cout << "\tTotal leakage power of a bank without power gating, including "
              "its network outside (mW): "
           << (g_ip->long_channel_device
                   ? fr->data_array2->power.readOp.leakage *
                         long_channel_leakage_reduction
                   : fr->data_array2->power.readOp.leakage) *
                  1e3;
      if (dvs) {
        cout << " (@DVS_Level0); ";
        for (i = 0; i < dvs_levels; i++)
          cout << (g_ip->long_channel_device
                       ? fr->uca_q[i]->data_array2->power.readOp.leakage *
                             long_channel_leakage_reduction
                       : fr->uca_q[i]->data_array2->power.readOp.leakage) *
                      1e3
               << " (@DVS_Level" << i + 1 << "_Vdd=" << g_ip->dvs_voltage[i]
               << "); ";
      }
      cout << endl;

      //    	}

      cout << "\tTotal energy in H-tree outside banks (that includes both "
              "address and data transfer) (nJ): "
           << (fr->data_array2->power_routing_to_bank.readOp.dynamic) * 1e9;
      if (dvs) {
        cout << " (@DVS_Level0); ";
        for (i = 0; i < dvs_levels; i++)
          cout << fr->uca_q[i]
                          ->data_array2->power_routing_to_bank.readOp.dynamic *
                      1e9
               << " (@DVS_Level" << i + 1 << "_Vdd=" << g_ip->dvs_voltage[i]
               << "); ";
      }
      cout << endl;

      cout << "\tInput H-tree inside bank Energy (nJ): "
           << (fr->data_array2->power_addr_input_htree.readOp.dynamic) * 1e9;
      if (dvs) {
        cout << " (@DVS_Level0); ";
        for (i = 0; i < dvs_levels; i++)
          cout << fr->uca_q[i]
                          ->data_array2->power_addr_input_htree.readOp.dynamic *
                      1e9
               << " (@DVS_Level" << i + 1 << "_Vdd=" << g_ip->dvs_voltage[i]
               << "); ";
      }
      cout << endl;

      cout << "\tOutput Htree inside bank Energy (nJ): "
           << fr->data_array2->power_data_output_htree.readOp.dynamic * 1e9;
      if (dvs) {
        cout << " (@DVS_Level0); ";
        for (i = 0; i < dvs_levels; i++)
          cout
              << fr->uca_q[i]
                         ->data_array2->power_data_output_htree.readOp.dynamic *
                     1e9
              << " (@DVS_Level" << i + 1 << "_Vdd=" << g_ip->dvs_voltage[i]
              << "); ";
      }
      cout << endl;

      cout << "\tDecoder (nJ): "
           << fr->data_array2->power_row_predecoder_drivers.readOp.dynamic *
                      1e9 +
                  fr->data_array2->power_row_predecoder_blocks.readOp.dynamic *
                      1e9;
      if (dvs) {
        cout << " (@DVS_Level0); ";
        for (i = 0; i < dvs_levels; i++)
          cout << (fr->uca_q[i]
                           ->data_array2->power_row_predecoder_drivers.readOp
                           .dynamic *
                       1e9 +
                   fr->uca_q[i]
                           ->data_array2->power_row_predecoder_blocks.readOp
                           .dynamic *
                       1e9)
               << " (@DVS_Level" << i + 1 << "_Vdd=" << g_ip->dvs_voltage[i]
               << "); ";
      }
      cout << endl;

      cout << "\tWordline (nJ): "
           << fr->data_array2->power_row_decoders.readOp.dynamic * 1e9;
      if (dvs) {
        cout << " (@DVS_Level0); ";
        for (i = 0; i < dvs_levels; i++)
          cout << fr->uca_q[i]->data_array2->power_row_decoders.readOp.dynamic *
                      1e9
               << " (@DVS_Level" << i + 1 << "_Vdd=" << g_ip->dvs_voltage[i]
               << "); ";
      }
      cout << endl;

      cout << "\tBitline mux & associated drivers (nJ): "
           << fr->data_array2->power_bit_mux_predecoder_drivers.readOp.dynamic *
                      1e9 +
                  fr->data_array2->power_bit_mux_predecoder_blocks.readOp
                          .dynamic *
                      1e9 +
                  fr->data_array2->power_bit_mux_decoders.readOp.dynamic * 1e9;
      if (dvs) {
        cout << " (@DVS_Level0); ";
        for (i = 0; i < dvs_levels; i++)
          cout
              << (fr->uca_q[i]
                          ->data_array2->power_bit_mux_predecoder_drivers.readOp
                          .dynamic *
                      1e9 +
                  fr->uca_q[i]
                          ->data_array2->power_bit_mux_predecoder_blocks.readOp
                          .dynamic *
                      1e9 +
                  fr->uca_q[i]
                          ->data_array2->power_bit_mux_decoders.readOp.dynamic *
                      1e9)
              << " (@DVS_Level" << i + 1 << "_Vdd=" << g_ip->dvs_voltage[i]
              << "); ";
      }
      cout << endl;

      cout << "\tSense amp mux & associated drivers (nJ): "
           << fr->data_array2->power_senseamp_mux_lev_1_predecoder_drivers
                          .readOp.dynamic *
                      1e9 +
                  fr->data_array2->power_senseamp_mux_lev_1_predecoder_blocks
                          .readOp.dynamic *
                      1e9 +
                  fr->data_array2->power_senseamp_mux_lev_1_decoders.readOp
                          .dynamic *
                      1e9 +
                  fr->data_array2->power_senseamp_mux_lev_2_predecoder_drivers
                          .readOp.dynamic *
                      1e9 +
                  fr->data_array2->power_senseamp_mux_lev_2_predecoder_blocks
                          .readOp.dynamic *
                      1e9 +
                  fr->data_array2->power_senseamp_mux_lev_2_decoders.readOp
                          .dynamic *
                      1e9;
      if (dvs) {
        cout << " (@DVS_Level0); ";
        for (i = 0; i < dvs_levels; i++)
          cout << (fr->uca_q[i]
                           ->data_array2
                           ->power_senseamp_mux_lev_1_predecoder_drivers.readOp
                           .dynamic *
                       1e9 +
                   fr->uca_q[i]
                           ->data_array2
                           ->power_senseamp_mux_lev_1_predecoder_blocks.readOp
                           .dynamic *
                       1e9 +
                   fr->uca_q[i]
                           ->data_array2->power_senseamp_mux_lev_1_decoders
                           .readOp.dynamic *
                       1e9 +
                   fr->uca_q[i]
                           ->data_array2
                           ->power_senseamp_mux_lev_2_predecoder_drivers.readOp
                           .dynamic *
                       1e9 +
                   fr->uca_q[i]
                           ->data_array2
                           ->power_senseamp_mux_lev_2_predecoder_blocks.readOp
                           .dynamic *
                       1e9 +
                   fr->uca_q[i]
                           ->data_array2->power_senseamp_mux_lev_2_decoders
                           .readOp.dynamic *
                       1e9)
               << " (@DVS_Level" << i + 1 << "_Vdd=" << g_ip->dvs_voltage[i]
               << "); ";
      }
      cout << endl;

      cout << "\tBitlines precharge and equalization circuit (nJ): "
           << fr->data_array2->power_prechg_eq_drivers.readOp.dynamic * 1e9;
      if (dvs) {
        cout << " (@DVS_Level0); ";
        for (i = 0; i < dvs_levels; i++)
          cout
              << fr->uca_q[i]
                         ->data_array2->power_prechg_eq_drivers.readOp.dynamic *
                     1e9
              << " (@DVS_Level" << i + 1 << "_Vdd=" << g_ip->dvs_voltage[i]
              << "); ";
      }
      cout << endl;

      cout << "\tBitlines (nJ): "
           << fr->data_array2->power_bitlines.readOp.dynamic * 1e9;
      if (dvs) {
        cout << " (@DVS_Level0); ";
        for (i = 0; i < dvs_levels; i++)
          cout << fr->uca_q[i]->data_array2->power_bitlines.readOp.dynamic * 1e9
               << " (@DVS_Level" << i + 1 << "_Vdd=" << g_ip->dvs_voltage[i]
               << "); ";
      }
      cout << endl;

      cout << "\tSense amplifier energy (nJ): "
           << fr->data_array2->power_sense_amps.readOp.dynamic * 1e9;
      if (dvs) {
        cout << " (@DVS_Level0); ";
        for (i = 0; i < dvs_levels; i++)
          cout << fr->uca_q[i]->data_array2->power_sense_amps.readOp.dynamic *
                      1e9
               << " (@DVS_Level" << i + 1 << "_Vdd=" << g_ip->dvs_voltage[i]
               << "); ";
      }
      cout << endl;

      cout << "\tSub-array output driver (nJ): "
           << fr->data_array2->power_output_drivers_at_subarray.readOp.dynamic *
                  1e9;
      if (dvs) {
        cout << " (@DVS_Level0); ";
        for (i = 0; i < dvs_levels; i++)
          cout << fr->uca_q[i]
                          ->data_array2->power_output_drivers_at_subarray.readOp
                          .dynamic *
                      1e9
               << " (@DVS_Level" << i + 1 << "_Vdd=" << g_ip->dvs_voltage[i]
               << "); ";
      }
      cout << endl;

      if (g_ip->power_gating) {
        cout << "\tTotal leakage power in H-tree outside a bank when power "
                "gated (that includes both "
                "address and data network) ((mW)): "
             << (g_ip->long_channel_device
                     ? fr->data_array2->power_routing_to_bank.readOp
                               .power_gated_leakage *
                           long_channel_leakage_reduction
                     : fr->data_array2->power_routing_to_bank.readOp
                           .power_gated_leakage) *
                    1e3
             << endl;
      }
      //    	else
      //    	{
      cout << "\tTotal leakage power in H-tree outside a bank (that includes "
              "both "
              "address and data network) ((mW)): "
           << (g_ip->long_channel_device
                   ? fr->data_array2->power_routing_to_bank.readOp.leakage *
                         long_channel_leakage_reduction
                   : fr->data_array2->power_routing_to_bank.readOp.leakage) *
                  1e3;
      if (dvs) {
        cout << " (@DVS_Level0); ";
        for (i = 0; i < dvs_levels; i++)
          cout << (g_ip->long_channel_device
                       ? fr->uca_q[i]
                                 ->data_array2->power_routing_to_bank.readOp
                                 .leakage *
                             long_channel_leakage_reduction
                       : fr->uca_q[i]
                             ->data_array2->power_routing_to_bank.readOp
                             .leakage) *
                      1e3
               << " (@DVS_Level" << i + 1 << "_Vdd=" << g_ip->dvs_voltage[i]
               << "); ";
      }
      cout << endl;

      //    	}
      //    	cout << "\tTotal leakage power in H-tree (that includes both "
      //    	      "address and data network) ((mW)): " <<
      //    	        (fr->data_array2->power_addr_input_htree.readOp.leakage
      //    + 	         fr->data_array2->power_data_output_htree.readOp.leakage
      //    + 	         fr->data_array2->power_routing_to_bank.readOp.leakage)
      //    * 1e3 << endl;

      //    	cout << "\tTotal leakage power in cells (mW): " <<
      //    	        (fr->data_array2->array_leakage) * 1e3 << endl;
      //    	cout << "\tTotal leakage power in row logic(mW): " <<
      //    	        (fr->data_array2->wl_leakage) * 1e3 << endl;
      //    	cout << "\tTotal leakage power in column logic(mW): " <<
      //    	        (fr->data_array2->cl_leakage) * 1e3 << endl;
      //
      //    	cout << "\tTotal gate leakage power in H-tree (that includes
      //    both " 	      "address and data network) ((mW)): " <<
      //    	        (fr->data_array2->power_addr_input_htree.readOp.gate_leakage
      //    +
      //    	         fr->data_array2->power_data_output_htree.readOp.gate_leakage
      //    +
      //    	         fr->data_array2->power_routing_to_bank.readOp.gate_leakage)
      //    * 1e3 << endl;
    }

    if (g_ip->pure_cam || g_ip->fully_assoc) {
      if (g_ip->pure_cam)
        cout << "  CAM array:" << endl;
      //            	cout << "  Total dynamic associative search
      //            energy/access  (nJ): " <<
      //                      fr->data_array2->power.searchOp.dynamic * 1e9 <<
      //                      endl;
      //    	        cout << "\tTotal energy in H-tree (that includes both "
      //    	            	      "match key and data transfer) (nJ): " <<
      //    	              (fr->data_array2->power_htree_in_search.searchOp.dynamic
      //    +
      //    	               fr->data_array2->power_htree_out_search.searchOp.dynamic
      //    +
      //    	               fr->data_array2->power_routing_to_bank.searchOp.dynamic)
      //    * 1e9 << endl; 	        cout << "\tKeyword input and result
      //    output Htrees inside bank Energy (nJ): " <<
      //    	              (fr->data_array2->power_htree_in_search.searchOp.dynamic
      //    +
      //    	       	               fr->data_array2->power_htree_out_search.searchOp.dynamic)
      //    * 1e9 << endl; 	        cout <<  "\tSearchlines (nJ): " <<
      //    	          	   fr->data_array2->power_searchline.searchOp.dynamic
      //    * 1e9 +
      //    	          	   fr->data_array2->power_searchline_precharge.searchOp.dynamic
      //    * 1e9 << endl; 	        cout <<  "\tMatchlines  (nJ): " <<
      //    	               fr->data_array2->power_matchlines.searchOp.dynamic
      //    * 1e9 +
      //    	        	   fr->data_array2->power_matchline_precharge.searchOp.dynamic
      //    * 1e9 << endl; 	        cout <<  "\tSub-array output driver
      //    (nJ): " <<
      //    	          	   fr->data_array2->power_output_drivers_at_subarray.searchOp.dynamic
      //    * 1e9 << endl;
      //
      //
      //            	cout <<endl<< "  Total dynamic read energy/access  (nJ):
      //            " << 	      fr->data_array2->power.readOp.dynamic *
      //            1e9 << endl;
      //    	        cout << "\tTotal energy in H-tree (that includes both "
      //    	            	      "address and data transfer) (nJ): " <<
      //    	              (fr->data_array2->power_addr_input_htree.readOp.dynamic
      //    +
      //    	               fr->data_array2->power_data_output_htree.readOp.dynamic
      //    +
      //    	               fr->data_array2->power_routing_to_bank.readOp.dynamic)
      //    * 1e9 << endl; 	        cout << "\tOutput Htree inside bank
      //    Energy (nJ): " <<
      //    	          	   fr->data_array2->power_data_output_htree.readOp.dynamic
      //    * 1e9 << endl; 	        cout <<  "\tDecoder (nJ): " <<
      //    	          	   fr->data_array2->power_row_predecoder_drivers.readOp.dynamic
      //    * 1e9 +
      //    	          	   fr->data_array2->power_row_predecoder_blocks.readOp.dynamic
      //    * 1e9 << endl; 	        cout <<  "\tWordline (nJ): " <<
      //    	          	   fr->data_array2->power_row_decoders.readOp.dynamic
      //    * 1e9 << endl; 	        cout <<  "\tBitline mux & associated
      //    drivers (nJ): " <<
      //    	          	   fr->data_array2->power_bit_mux_predecoder_drivers.readOp.dynamic
      //    * 1e9 +
      //    	          	   fr->data_array2->power_bit_mux_predecoder_blocks.readOp.dynamic
      //    * 1e9 +
      //    	           	   fr->data_array2->power_bit_mux_decoders.readOp.dynamic
      //    * 1e9 << endl; 	        cout <<  "\tSense amp mux & associated
      //    drivers (nJ): " <<
      //    	         	   fr->data_array2->power_senseamp_mux_lev_1_predecoder_drivers.readOp.dynamic
      //    * 1e9 +
      //    	          	   fr->data_array2->power_senseamp_mux_lev_1_predecoder_blocks.readOp.dynamic
      //    * 1e9 +
      //    	          	   fr->data_array2->power_senseamp_mux_lev_1_decoders.readOp.dynamic
      //    * 1e9  +
      //    	           	   fr->data_array2->power_senseamp_mux_lev_2_predecoder_drivers.readOp.dynamic
      //    * 1e9 +
      //    	           	   fr->data_array2->power_senseamp_mux_lev_2_predecoder_blocks.readOp.dynamic
      //    * 1e9 +
      //    	          	   fr->data_array2->power_senseamp_mux_lev_2_decoders.readOp.dynamic
      //    * 1e9 << endl; 	        cout <<  "\tBitlines (nJ): " <<
      //    	          	   fr->data_array2->power_bitlines.readOp.dynamic
      //    * 1e9 +
      //    	          	   fr->data_array2->power_prechg_eq_drivers.readOp.dynamic
      //    * 1e9<< endl; 	        cout <<  "\tSense amplifier energy (nJ):
      //    " <<
      //    	          	   fr->data_array2->power_sense_amps.readOp.dynamic
      //    * 1e9 << endl; 	        cout <<  "\tSub-array output driver
      //    (nJ): " <<
      //    	          	   fr->data_array2->power_output_drivers_at_subarray.readOp.dynamic
      //    * 1e9 << endl;
      //
      //            	cout << endl <<"  Total leakage power of a bank (mW): "
      //            <<
      //                      fr->data_array2->power.readOp.leakage * 1e3 <<
      //                      endl;
      //        }
      //        else
      //        {
      if (g_ip->fully_assoc)
        cout << "  Fully associative array:" << endl;

      cout << "  Total dynamic associative search energy/access  (nJ): "
           << fr->data_array2->power.searchOp.dynamic * 1e9;
      if (dvs) {
        cout << " (@DVS_Level0); ";
        for (i = 0; i < dvs_levels; i++)
          cout << fr->uca_q[i]->data_array2->power.searchOp.dynamic * 1e9
               << " (@DVS_Level" << i + 1 << "_Vdd=" << g_ip->dvs_voltage[i]
               << "); ";
      }
      cout << endl;

      cout << "\tTotal energy in H-tree outside banks(that includes both "
              "match key and data transfer) (nJ): "
           << (fr->data_array2->power_routing_to_bank.searchOp.dynamic) * 1e9;
      if (dvs) {
        cout << " (@DVS_Level0); ";
        for (i = 0; i < dvs_levels; i++)
          cout
              << fr->uca_q[i]
                         ->data_array2->power_routing_to_bank.searchOp.dynamic *
                     1e9
              << " (@DVS_Level" << i + 1 << "_Vdd=" << g_ip->dvs_voltage[i]
              << "); ";
      }
      cout << endl;

      cout << "\tMatch Key input Htrees inside bank Energy (nJ): "
           << (fr->data_array2->power_htree_in_search.searchOp.dynamic) * 1e9;
      if (dvs) {
        cout << " (@DVS_Level0); ";
        for (i = 0; i < dvs_levels; i++)
          cout
              << fr->uca_q[i]
                         ->data_array2->power_htree_in_search.searchOp.dynamic *
                     1e9
              << " (@DVS_Level" << i + 1 << "_Vdd=" << g_ip->dvs_voltage[i]
              << "); ";
      }
      cout << endl;

      cout << "\tResult output Htrees inside bank Energy (nJ): "
           << (fr->data_array2->power_htree_out_search.searchOp.dynamic) * 1e9;
      if (dvs) {
        cout << " (@DVS_Level0); ";
        for (i = 0; i < dvs_levels; i++)
          cout << fr->uca_q[i]
                          ->data_array2->power_htree_out_search.searchOp
                          .dynamic *
                      1e9
               << " (@DVS_Level" << i + 1 << "_Vdd=" << g_ip->dvs_voltage[i]
               << "); ";
      }
      cout << endl;

      cout << "\tSearchlines (nJ): "
           << fr->data_array2->power_searchline.searchOp.dynamic * 1e9 +
                  fr->data_array2->power_searchline_precharge.searchOp.dynamic *
                      1e9;
      if (dvs) {
        cout << " (@DVS_Level0); ";
        for (i = 0; i < dvs_levels; i++)
          cout << fr->uca_q[i]->data_array2->power_searchline.searchOp.dynamic *
                          1e9 +
                      fr->uca_q[i]
                              ->data_array2->power_searchline_precharge.searchOp
                              .dynamic *
                          1e9
               << " (@DVS_Level" << i + 1 << "_Vdd=" << g_ip->dvs_voltage[i]
               << "); ";
      }
      cout << endl;

      cout << "\tMatchlines  (nJ): "
           << fr->data_array2->power_matchlines.searchOp.dynamic * 1e9 +
                  fr->data_array2->power_matchline_precharge.searchOp.dynamic *
                      1e9;
      if (dvs) {
        cout << " (@DVS_Level0); ";
        for (i = 0; i < dvs_levels; i++)
          cout << fr->uca_q[i]->data_array2->power_matchlines.searchOp.dynamic *
                          1e9 +
                      fr->uca_q[i]
                              ->data_array2->power_matchline_precharge.searchOp
                              .dynamic *
                          1e9
               << " (@DVS_Level" << i + 1 << "_Vdd=" << g_ip->dvs_voltage[i]
               << "); ";
      }
      cout << endl;

      if (g_ip->fully_assoc) {
        cout << "\tData portion wordline (nJ): "
             << fr->data_array2->power_matchline_to_wordline_drv.searchOp
                        .dynamic *
                    1e9;
        if (dvs) {
          cout << " (@DVS_Level0); ";
          for (i = 0; i < dvs_levels; i++)
            cout << fr->uca_q[i]
                            ->data_array2->power_matchline_to_wordline_drv
                            .searchOp.dynamic *
                        1e9
                 << " (@DVS_Level" << i + 1 << "_Vdd=" << g_ip->dvs_voltage[i]
                 << "); ";
        }
        cout << endl;

        cout << "\tData Bitlines (nJ): "
             << fr->data_array2->power_bitlines.searchOp.dynamic * 1e9 +
                    fr->data_array2->power_prechg_eq_drivers.searchOp.dynamic *
                        1e9;
        if (dvs) {
          cout << " (@DVS_Level0); ";
          for (i = 0; i < dvs_levels; i++)
            cout
                << (fr->uca_q[i]->data_array2->power_bitlines.searchOp.dynamic *
                        1e9 +
                    fr->uca_q[i]
                            ->data_array2->power_prechg_eq_drivers.searchOp
                            .dynamic *
                        1e9)
                << " (@DVS_Level" << i + 1 << "_Vdd=" << g_ip->dvs_voltage[i]
                << "); ";
        }
        cout << endl;

        cout << "\tSense amplifier energy (nJ): "
             << fr->data_array2->power_sense_amps.searchOp.dynamic * 1e9;
        if (dvs) {
          cout << " (@DVS_Level0); ";
          for (i = 0; i < dvs_levels; i++)
            cout << fr->uca_q[i]
                            ->data_array2->power_sense_amps.searchOp.dynamic *
                        1e9
                 << " (@DVS_Level" << i + 1 << "_Vdd=" << g_ip->dvs_voltage[i]
                 << "); ";
        }
        cout << endl;
      }

      cout << "\tSub-array output driver (nJ): "
           << fr->data_array2->power_output_drivers_at_subarray.searchOp
                      .dynamic *
                  1e9;
      if (dvs) {
        cout << " (@DVS_Level0); ";
        for (i = 0; i < dvs_levels; i++)
          cout << fr->uca_q[i]
                          ->data_array2->power_output_drivers_at_subarray
                          .searchOp.dynamic *
                      1e9
               << " (@DVS_Level" << i + 1 << "_Vdd=" << g_ip->dvs_voltage[i]
               << "); ";
      }
      cout << endl;

      cout << endl
           << "  Total dynamic read energy/access  (nJ): "
           << fr->data_array2->power.readOp.dynamic * 1e9;
      if (dvs) {
        cout << " (@DVS_Level0); ";
        for (i = 0; i < dvs_levels; i++)
          cout << fr->uca_q[i]->data_array2->power.readOp.dynamic * 1e9
               << " (@DVS_Level" << i + 1 << "_Vdd=" << g_ip->dvs_voltage[i]
               << "); ";
      }
      cout << endl;

      cout << "\tTotal energy in H-tree outside banks(that includes both "
              "address and data transfer) (nJ): "
           << (fr->data_array2->power_routing_to_bank.readOp.dynamic) * 1e9;
      if (dvs) {
        cout << " (@DVS_Level0); ";
        for (i = 0; i < dvs_levels; i++)
          cout << fr->uca_q[i]
                          ->data_array2->power_routing_to_bank.readOp.dynamic *
                      1e9
               << " (@DVS_Level" << i + 1 << "_Vdd=" << g_ip->dvs_voltage[i]
               << "); ";
      }
      cout << endl;

      cout << "\tInput Htree inside bank Energy (nJ): "
           << (fr->data_array2->power_addr_input_htree.readOp.dynamic) * 1e9;
      if (dvs) {
        cout << " (@DVS_Level0); ";
        for (i = 0; i < dvs_levels; i++)
          cout << fr->uca_q[i]
                          ->data_array2->power_addr_input_htree.readOp.dynamic *
                      1e9
               << " (@DVS_Level" << i + 1 << "_Vdd=" << g_ip->dvs_voltage[i]
               << "); ";
      }
      cout << endl;

      cout << "\tOutput Htree inside bank Energy (nJ): "
           << fr->data_array2->power_data_output_htree.readOp.dynamic * 1e9;
      if (dvs) {
        cout << " (@DVS_Level0); ";
        for (i = 0; i < dvs_levels; i++)
          cout
              << fr->uca_q[i]
                         ->data_array2->power_data_output_htree.readOp.dynamic *
                     1e9
              << " (@DVS_Level" << i + 1 << "_Vdd=" << g_ip->dvs_voltage[i]
              << "); ";
      }
      cout << endl;

      cout << "\tDecoder (nJ): "
           << fr->data_array2->power_row_predecoder_drivers.readOp.dynamic *
                      1e9 +
                  fr->data_array2->power_row_predecoder_blocks.readOp.dynamic *
                      1e9;
      if (dvs) {
        cout << " (@DVS_Level0); ";
        for (i = 0; i < dvs_levels; i++)
          cout << (fr->uca_q[i]
                           ->data_array2->power_row_predecoder_drivers.readOp
                           .dynamic *
                       1e9 +
                   fr->uca_q[i]
                           ->data_array2->power_row_predecoder_blocks.readOp
                           .dynamic *
                       1e9)
               << " (@DVS_Level" << i + 1 << "_Vdd=" << g_ip->dvs_voltage[i]
               << "); ";
      }
      cout << endl;

      cout << "\tWordline (nJ): "
           << fr->data_array2->power_row_decoders.readOp.dynamic * 1e9;
      if (dvs) {
        cout << " (@DVS_Level0); ";
        for (i = 0; i < dvs_levels; i++)
          cout << fr->uca_q[i]->data_array2->power_row_decoders.readOp.dynamic *
                      1e9
               << " (@DVS_Level" << i + 1 << "_Vdd=" << g_ip->dvs_voltage[i]
               << "); ";
      }
      cout << endl;

      cout << "\tBitline mux & associated drivers (nJ): "
           << fr->data_array2->power_bit_mux_predecoder_drivers.readOp.dynamic *
                      1e9 +
                  fr->data_array2->power_bit_mux_predecoder_blocks.readOp
                          .dynamic *
                      1e9 +
                  fr->data_array2->power_bit_mux_decoders.readOp.dynamic * 1e9;
      if (dvs) {
        cout << " (@DVS_Level0); ";
        for (i = 0; i < dvs_levels; i++)
          cout
              << (fr->uca_q[i]
                          ->data_array2->power_bit_mux_predecoder_drivers.readOp
                          .dynamic *
                      1e9 +
                  fr->uca_q[i]
                          ->data_array2->power_bit_mux_predecoder_blocks.readOp
                          .dynamic *
                      1e9 +
                  fr->uca_q[i]
                          ->data_array2->power_bit_mux_decoders.readOp.dynamic *
                      1e9)
              << " (@DVS_Level" << i + 1 << "_Vdd=" << g_ip->dvs_voltage[i]
              << "); ";
      }
      cout << endl;

      cout << "\tSense amp mux & associated drivers (nJ): "
           << fr->data_array2->power_senseamp_mux_lev_1_predecoder_drivers
                          .readOp.dynamic *
                      1e9 +
                  fr->data_array2->power_senseamp_mux_lev_1_predecoder_blocks
                          .readOp.dynamic *
                      1e9 +
                  fr->data_array2->power_senseamp_mux_lev_1_decoders.readOp
                          .dynamic *
                      1e9 +
                  fr->data_array2->power_senseamp_mux_lev_2_predecoder_drivers
                          .readOp.dynamic *
                      1e9 +
                  fr->data_array2->power_senseamp_mux_lev_2_predecoder_blocks
                          .readOp.dynamic *
                      1e9 +
                  fr->data_array2->power_senseamp_mux_lev_2_decoders.readOp
                          .dynamic *
                      1e9;
      if (dvs) {
        cout << " (@DVS_Level0); ";
        for (i = 0; i < dvs_levels; i++)
          cout << (fr->uca_q[i]
                           ->data_array2
                           ->power_senseamp_mux_lev_1_predecoder_drivers.readOp
                           .dynamic *
                       1e9 +
                   fr->uca_q[i]
                           ->data_array2
                           ->power_senseamp_mux_lev_1_predecoder_blocks.readOp
                           .dynamic *
                       1e9 +
                   fr->uca_q[i]
                           ->data_array2->power_senseamp_mux_lev_1_decoders
                           .readOp.dynamic *
                       1e9 +
                   fr->uca_q[i]
                           ->data_array2
                           ->power_senseamp_mux_lev_2_predecoder_drivers.readOp
                           .dynamic *
                       1e9 +
                   fr->uca_q[i]
                           ->data_array2
                           ->power_senseamp_mux_lev_2_predecoder_blocks.readOp
                           .dynamic *
                       1e9 +
                   fr->uca_q[i]
                           ->data_array2->power_senseamp_mux_lev_2_decoders
                           .readOp.dynamic *
                       1e9)
               << " (@DVS_Level" << i + 1 << "_Vdd=" << g_ip->dvs_voltage[i]
               << "); ";
      }
      cout << endl;

      cout << "\tBitlines (nJ): "
           << fr->data_array2->power_bitlines.readOp.dynamic * 1e9 +
                  fr->data_array2->power_prechg_eq_drivers.readOp.dynamic * 1e9;
      if (dvs) {
        cout << " (@DVS_Level0); ";
        for (i = 0; i < dvs_levels; i++)
          cout << (fr->uca_q[i]->data_array2->power_bitlines.readOp.dynamic *
                       1e9 +
                   fr->uca_q[i]
                           ->data_array2->power_prechg_eq_drivers.readOp
                           .dynamic *
                       1e9)
               << " (@DVS_Level" << i + 1 << "_Vdd=" << g_ip->dvs_voltage[i]
               << "); ";
      }
      cout << endl;

      cout << "\tSense amplifier energy (nJ): "
           << fr->data_array2->power_sense_amps.readOp.dynamic * 1e9;
      if (dvs) {
        cout << " (@DVS_Level0); ";
        for (i = 0; i < dvs_levels; i++)
          cout << fr->uca_q[i]->data_array2->power_sense_amps.readOp.dynamic *
                      1e9
               << " (@DVS_Level" << i + 1 << "_Vdd=" << g_ip->dvs_voltage[i]
               << "); ";
      }
      cout << endl;

      cout << "\tSub-array output driver (nJ): "
           << fr->data_array2->power_output_drivers_at_subarray.readOp.dynamic *
                  1e9;
      if (dvs) {
        cout << " (@DVS_Level0); ";
        for (i = 0; i < dvs_levels; i++)
          cout << fr->uca_q[i]
                          ->data_array2->power_output_drivers_at_subarray.readOp
                          .dynamic *
                      1e9
               << " (@DVS_Level" << i + 1 << "_Vdd=" << g_ip->dvs_voltage[i]
               << "); ";
      }
      cout << endl;

      cout << endl
           << "  Total leakage power of a bank, including its network outside "
              "(mW): "
           << (g_ip->long_channel_device
                   ? fr->data_array2->power.readOp.leakage *
                         long_channel_leakage_reduction
                   : fr->data_array2->power.readOp.leakage) *
                  1e3;  // CAM/FA does not support PG yet
      if (dvs) {
        cout << " (@DVS_Level0); ";
        for (i = 0; i < dvs_levels; i++)
          cout << (g_ip->long_channel_device
                       ? fr->uca_q[i]->data_array2->power.readOp.leakage *
                             long_channel_leakage_reduction
                       : fr->uca_q[i]->data_array2->power.readOp.leakage) *
                      1e3
               << " (@DVS_Level" << i + 1 << "_Vdd=" << g_ip->dvs_voltage[i]
               << "); ";
      }
      cout << endl;
    }

    if ((!(g_ip->pure_ram || g_ip->pure_cam || g_ip->fully_assoc)) &&
        !g_ip->is_main_mem) {
      cout << endl
           << "  Tag array:  Total dynamic read energy/access (nJ): "
           << fr->tag_array2->power.readOp.dynamic * 1e9;
      if (dvs) {
        cout << " (@DVS_Level0); ";
        for (i = 0; i < dvs_levels; i++)
          cout << fr->uca_q[i]->tag_array2->power.readOp.dynamic * 1e9
               << " (@DVS_Level" << i + 1 << "_Vdd=" << g_ip->dvs_voltage[i]
               << "); ";
      }
      cout << endl;

      if (g_ip->power_gating) {
        cout << "\tTotal leakage power of a bank, power gated ";
        if (!g_ip->user_defined_vcc_underflow) {
          cout << "with ";
        }
        else {
          cout << "without ";
        }
        cout << "retaining memory content, including its network outside (mW): "
             << (g_ip->long_channel_device
                     ? fr->tag_array2->power.readOp.power_gated_leakage *
                           long_channel_leakage_reduction
                     : fr->tag_array2->power.readOp.power_gated_leakage) *
                    1e3
             << endl;
      }
      //            else
      //            {
      cout << "\tTotal leakage power of a bank without power gating, including "
              "its network outside (mW): "
           << (g_ip->long_channel_device
                   ? fr->tag_array2->power.readOp.leakage *
                         long_channel_leakage_reduction
                   : fr->tag_array2->power.readOp.leakage) *
                  1e3;
      if (dvs) {
        cout << " (@DVS_Level0); ";
        for (i = 0; i < dvs_levels; i++)
          cout << (g_ip->long_channel_device
                       ? fr->uca_q[i]->tag_array2->power.readOp.leakage *
                             long_channel_leakage_reduction
                       : fr->uca_q[i]->tag_array2->power.readOp.leakage) *
                      1e3
               << " (@DVS_Level" << i + 1 << "_Vdd=" << g_ip->dvs_voltage[i]
               << "); ";
      }
      cout << endl;

      //            }
      //      cout << "\tTotal leakage read/write power of a bank (mW): " <<
      //          fr->tag_array2->power.readOp.leakage * 1e3 << endl;
      cout << "\tTotal energy in H-tree outside banks (that includes both "
              "address and data transfer) (nJ): "
           << (fr->tag_array2->power_routing_to_bank.readOp.dynamic) * 1e9;
      if (dvs) {
        cout << " (@DVS_Level0); ";
        for (i = 0; i < dvs_levels; i++)
          cout << fr->uca_q[i]
                          ->tag_array2->power_routing_to_bank.readOp.dynamic *
                      1e9
               << " (@DVS_Level" << i + 1 << "_Vdd=" << g_ip->dvs_voltage[i]
               << "); ";
      }
      cout << endl;

      cout << "\tInput H-tree inside banks Energy (nJ): "
           << (fr->tag_array2->power_addr_input_htree.readOp.dynamic) * 1e9;
      if (dvs) {
        cout << " (@DVS_Level0); ";
        for (i = 0; i < dvs_levels; i++)
          cout << fr->uca_q[i]
                          ->tag_array2->power_addr_input_htree.readOp.dynamic *
                      1e9
               << " (@DVS_Level" << i + 1 << "_Vdd=" << g_ip->dvs_voltage[i]
               << "); ";
      }
      cout << endl;

      cout << "\tOutput Htree inside a bank Energy (nJ): "
           << fr->tag_array2->power_data_output_htree.readOp.dynamic * 1e9;
      if (dvs) {
        cout << " (@DVS_Level0); ";
        for (i = 0; i < dvs_levels; i++)
          cout << fr->uca_q[i]
                          ->tag_array2->power_data_output_htree.readOp.dynamic *
                      1e9
               << " (@DVS_Level" << i + 1 << "_Vdd=" << g_ip->dvs_voltage[i]
               << "); ";
      }
      cout << endl;

      cout << "\tDecoder (nJ): "
           << fr->tag_array2->power_row_predecoder_drivers.readOp.dynamic *
                      1e9 +
                  fr->tag_array2->power_row_predecoder_blocks.readOp.dynamic *
                      1e9;
      if (dvs) {
        cout << " (@DVS_Level0); ";
        for (i = 0; i < dvs_levels; i++)
          cout << (fr->uca_q[i]
                           ->tag_array2->power_row_predecoder_drivers.readOp
                           .dynamic *
                       1e9 +
                   fr->uca_q[i]
                           ->tag_array2->power_row_predecoder_blocks.readOp
                           .dynamic *
                       1e9)
               << " (@DVS_Level" << i + 1 << "_Vdd=" << g_ip->dvs_voltage[i]
               << "); ";
      }
      cout << endl;

      cout << "\tWordline (nJ): "
           << fr->tag_array2->power_row_decoders.readOp.dynamic * 1e9;
      if (dvs) {
        cout << " (@DVS_Level0); ";
        for (i = 0; i < dvs_levels; i++)
          cout << fr->uca_q[i]->tag_array2->power_row_decoders.readOp.dynamic *
                      1e9
               << " (@DVS_Level" << i + 1 << "_Vdd=" << g_ip->dvs_voltage[i]
               << "); ";
      }
      cout << endl;

      cout << "\tBitline mux & associated drivers (nJ): "
           << fr->tag_array2->power_bit_mux_predecoder_drivers.readOp.dynamic *
                      1e9 +
                  fr->tag_array2->power_bit_mux_predecoder_blocks.readOp
                          .dynamic *
                      1e9 +
                  fr->tag_array2->power_bit_mux_decoders.readOp.dynamic * 1e9;
      if (dvs) {
        cout << " (@DVS_Level0); ";
        for (i = 0; i < dvs_levels; i++)
          cout << (fr->uca_q[i]
                           ->tag_array2->power_bit_mux_predecoder_drivers.readOp
                           .dynamic *
                       1e9 +
                   fr->uca_q[i]
                           ->tag_array2->power_bit_mux_predecoder_blocks.readOp
                           .dynamic *
                       1e9 +
                   fr->uca_q[i]
                           ->tag_array2->power_bit_mux_decoders.readOp.dynamic *
                       1e9)
               << " (@DVS_Level" << i + 1 << "_Vdd=" << g_ip->dvs_voltage[i]
               << "); ";
      }
      cout << endl;

      cout << "\tSense amp mux & associated drivers (nJ): "
           << fr->tag_array2->power_senseamp_mux_lev_1_predecoder_drivers.readOp
                          .dynamic *
                      1e9 +
                  fr->tag_array2->power_senseamp_mux_lev_1_predecoder_blocks
                          .readOp.dynamic *
                      1e9 +
                  fr->tag_array2->power_senseamp_mux_lev_1_decoders.readOp
                          .dynamic *
                      1e9 +
                  fr->tag_array2->power_senseamp_mux_lev_2_predecoder_drivers
                          .readOp.dynamic *
                      1e9 +
                  fr->tag_array2->power_senseamp_mux_lev_2_predecoder_blocks
                          .readOp.dynamic *
                      1e9 +
                  fr->tag_array2->power_senseamp_mux_lev_2_decoders.readOp
                          .dynamic *
                      1e9;
      if (dvs) {
        cout << " (@DVS_Level0); ";
        for (i = 0; i < dvs_levels; i++)
          cout << (fr->uca_q[i]
                           ->tag_array2
                           ->power_senseamp_mux_lev_1_predecoder_drivers.readOp
                           .dynamic *
                       1e9 +
                   fr->uca_q[i]
                           ->tag_array2
                           ->power_senseamp_mux_lev_1_predecoder_blocks.readOp
                           .dynamic *
                       1e9 +
                   fr->uca_q[i]
                           ->tag_array2->power_senseamp_mux_lev_1_decoders
                           .readOp.dynamic *
                       1e9 +
                   fr->uca_q[i]
                           ->tag_array2
                           ->power_senseamp_mux_lev_2_predecoder_drivers.readOp
                           .dynamic *
                       1e9 +
                   fr->uca_q[i]
                           ->tag_array2
                           ->power_senseamp_mux_lev_2_predecoder_blocks.readOp
                           .dynamic *
                       1e9 +
                   fr->uca_q[i]
                           ->tag_array2->power_senseamp_mux_lev_2_decoders
                           .readOp.dynamic *
                       1e9)
               << " (@DVS_Level" << i + 1 << "_Vdd=" << g_ip->dvs_voltage[i]
               << "); ";
      }
      cout << endl;

      cout << "\tBitlines precharge and equalization circuit (nJ): "
           << fr->tag_array2->power_prechg_eq_drivers.readOp.dynamic * 1e9;
      if (dvs) {
        cout << " (@DVS_Level0); ";
        for (i = 0; i < dvs_levels; i++)
          cout << fr->uca_q[i]
                          ->tag_array2->power_prechg_eq_drivers.readOp.dynamic *
                      1e9
               << " (@DVS_Level" << i + 1 << "_Vdd=" << g_ip->dvs_voltage[i]
               << "); ";
      }
      cout << endl;
      cout << "\tBitlines (nJ): "
           << fr->tag_array2->power_bitlines.readOp.dynamic * 1e9;
      if (dvs) {
        cout << " (@DVS_Level0); ";
        for (i = 0; i < dvs_levels; i++)
          cout << fr->uca_q[i]->tag_array2->power_bitlines.readOp.dynamic * 1e9
               << " (@DVS_Level" << i + 1 << "_Vdd=" << g_ip->dvs_voltage[i]
               << "); ";
      }
      cout << endl;

      cout << "\tSense amplifier energy (nJ): "
           << fr->tag_array2->power_sense_amps.readOp.dynamic * 1e9;
      if (dvs) {
        cout << " (@DVS_Level0); ";
        for (i = 0; i < dvs_levels; i++)
          cout << fr->uca_q[i]->tag_array2->power_sense_amps.readOp.dynamic *
                      1e9
               << " (@DVS_Level" << i + 1 << "_Vdd=" << g_ip->dvs_voltage[i]
               << "); ";
      }
      cout << endl;

      cout << "\tSub-array output driver (nJ): "
           << fr->tag_array2->power_output_drivers_at_subarray.readOp.dynamic *
                  1e9;
      if (dvs) {
        cout << " (@DVS_Level0); ";
        for (i = 0; i < dvs_levels; i++)
          cout << fr->uca_q[i]
                          ->tag_array2->power_output_drivers_at_subarray.readOp
                          .dynamic *
                      1e9
               << " (@DVS_Level" << i + 1 << "_Vdd=" << g_ip->dvs_voltage[i]
               << "); ";
      }
      cout << endl;

      if (g_ip->power_gating) {
        cout << "\tTotal leakage power in H-tree outside a bank when power "
                "gated (that includes both "
                "address and data network) ((mW)): "
             << (g_ip->long_channel_device
                     ? fr->tag_array2->power_routing_to_bank.readOp
                               .power_gated_leakage *
                           long_channel_leakage_reduction
                     : fr->tag_array2->power_routing_to_bank.readOp
                           .power_gated_leakage) *
                    1e3
             << endl;
      }
      //      else
      //      {
      cout << "\tTotal leakage power in H-tree outside a bank (that includes "
              "both "
              "address and data network) without power gating((mW)): "
           << (g_ip->long_channel_device
                   ? fr->tag_array2->power_routing_to_bank.readOp.leakage *
                         long_channel_leakage_reduction
                   : fr->tag_array2->power_routing_to_bank.readOp.leakage) *
                  1e3;
      if (dvs) {
        cout << " (@DVS_Level0); ";
        for (i = 0; i < dvs_levels; i++)
          cout << (g_ip->long_channel_device
                       ? fr->uca_q[i]
                                 ->tag_array2->power_routing_to_bank.readOp
                                 .leakage *
                             long_channel_leakage_reduction
                       : fr->uca_q[i]
                             ->tag_array2->power_routing_to_bank.readOp
                             .leakage) *
                      1e3
               << " (@DVS_Level" << i + 1 << "_Vdd=" << g_ip->dvs_voltage[i]
               << "); ";
      }
      cout << endl;
      //      }

      //    	cout << "\tTotal leakage power of a bank (mW): " <<
      //    	        fr->tag_array2->power.readOp.leakage * 1e3 << endl;
      //    	cout << "\tTotal leakage power in H-tree (that includes both "
      //    	      "address and data network) ((mW)): " <<
      //    	        (fr->tag_array2->power_addr_input_htree.readOp.leakage +
      //    	         fr->tag_array2->power_data_output_htree.readOp.leakage
      //    + 	         fr->tag_array2->power_routing_to_bank.readOp.leakage) *
      //    1e3 << endl;
      //
      //    	cout << "\tTotal leakage power in cells (mW): " <<
      //    	        (fr->tag_array2->array_leakage) * 1e3 << endl;
      //    	cout << "\tTotal leakage power in row logic(mW): " <<
      //    	        (fr->tag_array2->wl_leakage) * 1e3 << endl;
      //    	cout << "\tTotal leakage power in column logic(mW): " <<
      //    	        (fr->tag_array2->cl_leakage) * 1e3 << endl;
      //    	cout << "\tTotal gate leakage power in H-tree (that includes
      //    both " 	      "address and data network) ((mW)): " <<
      //    	        (fr->tag_array2->power_addr_input_htree.readOp.gate_leakage
      //    +
      //    	         fr->tag_array2->power_data_output_htree.readOp.gate_leakage
      //    +
      //    	         fr->tag_array2->power_routing_to_bank.readOp.gate_leakage)
      //    * 1e3 << endl;
    }

    cout << endl << endl << "Area Components:" << endl << endl;
    /* Data array area stats */
    if (!(g_ip->pure_cam || g_ip->fully_assoc))
      cout << "  Data array: Area (mm2): " << fr->data_array2->area * 1e-6
           << endl;
    else if (g_ip->pure_cam)
      cout << "  CAM array: Area (mm2): " << fr->data_array2->area * 1e-6
           << endl;
    else
      cout << "  Fully associative cache array: Area (mm2): "
           << fr->data_array2->area * 1e-6 << endl;

    cout << "\tHeight (mm): " << fr->data_array2->all_banks_height * 1e-3
         << endl;
    cout << "\tWidth (mm): " << fr->data_array2->all_banks_width * 1e-3 << endl;
    if (g_ip->print_detail) {
      cout << "\tArea efficiency (Memory cell area/Total area) - "
           << fr->data_array2->area_efficiency << " %" << endl;
      cout << "\t\tMAT Height (mm): " << fr->data_array2->mat_height * 1e-3
           << endl;
      cout << "\t\tMAT Length (mm): " << fr->data_array2->mat_length * 1e-3
           << endl;
      cout << "\t\tSubarray Height (mm): "
           << fr->data_array2->subarray_height * 1e-3 << endl;
      cout << "\t\tSubarray Length (mm): "
           << fr->data_array2->subarray_length * 1e-3 << endl;
      if (g_ip->power_gating) {
        overhead_data =
            (fr->data_array2->area / fr->uca_pg_reference->data_array2->area -
             1) *
            100;  //%;
        cout << "  Power gating circuits (sleep transistors) induced area "
                "overhead: "
             << overhead_data << "%" << endl;
      }
    }

    /* Tag array area stats */
    if ((!(g_ip->pure_ram || g_ip->pure_cam || g_ip->fully_assoc)) &&
        !g_ip->is_main_mem) {
      cout << endl
           << "  Tag array: Area (mm2): " << fr->tag_array2->area * 1e-6
           << endl;
      cout << "\tHeight (mm): " << fr->tag_array2->all_banks_height * 1e-3
           << endl;
      cout << "\tWidth (mm): " << fr->tag_array2->all_banks_width * 1e-3
           << endl;

      if (g_ip->print_detail) {
        cout << "\tArea efficiency (Memory cell area/Total area) - "
             << fr->tag_array2->area_efficiency << " %" << endl;
        cout << "\t\tMAT Height (mm): " << fr->tag_array2->mat_height * 1e-3
             << endl;
        cout << "\t\tMAT Length (mm): " << fr->tag_array2->mat_length * 1e-3
             << endl;
        cout << "\t\tSubarray Height (mm): "
             << fr->tag_array2->subarray_height * 1e-3 << endl;
        cout << "\t\tSubarray Length (mm): "
             << fr->tag_array2->subarray_length * 1e-3 << endl;
      }

      if (g_ip->power_gating) {
        overhead_tag =
            (fr->tag_array2->area / fr->uca_pg_reference->tag_array2->area -
             1) *
            100;  //%;
        cout << "  Power gating circuits (sleep transistors) induced area "
                "overhead: "
             << overhead_tag << "%" << endl
             << endl;
      }
    }

    // Wire wpr; //TODO: this must change, since this changes the wire value
    // during dvs loop.  Wire::print_wire();//move outside output UCA

    // cout << "FO4 = " << g_tp.FO4 << endl;
  }
}

// McPAT's plain interface, please keep !!!
uca_org_t cacti_interface(InputParameter *const local_interface) {
  //  g_ip = new InputParameter();
  // g_ip->add_ecc_b_ = true;

  uca_org_t fin_res;
  fin_res.valid = false;

  g_ip = local_interface;

  //  g_ip->data_arr_ram_cell_tech_type    = data_arr_ram_cell_tech_flavor_in;
  //  g_ip->data_arr_peri_global_tech_type =
  //  data_arr_peri_global_tech_flavor_in; g_ip->tag_arr_ram_cell_tech_type
  //  = tag_arr_ram_cell_tech_flavor_in; g_ip->tag_arr_peri_global_tech_type  =
  //  tag_arr_peri_global_tech_flavor_in;
  //
  //  g_ip->ic_proj_type     = interconnect_projection_type_in;
  //  g_ip->wire_is_mat_type = wire_inside_mat_type_in;
  //  g_ip->wire_os_mat_type = wire_outside_mat_type_in;
  //  g_ip->burst_len        = BURST_LENGTH_in;
  //  g_ip->int_prefetch_w   = INTERNAL_PREFETCH_WIDTH_in;
  //  g_ip->page_sz_bits     = PAGE_SIZE_BITS_in;
  //
  //  g_ip->cache_sz            = cache_size;
  //  g_ip->line_sz             = line_size;
  //  g_ip->assoc               = associativity;
  //  g_ip->nbanks              = banks;
  //  g_ip->out_w               = output_width;
  //  g_ip->specific_tag        = specific_tag;
  //  if (tag_width == 0) {
  //    g_ip->tag_w = 42;
  //  }
  //  else {
  //    g_ip->tag_w               = tag_width;
  //  }
  //
  //  g_ip->access_mode         = access_mode;
  //  g_ip->delay_wt = obj_func_delay;
  //  g_ip->dynamic_power_wt = obj_func_dynamic_power;
  //  g_ip->leakage_power_wt = obj_func_leakage_power;
  //  g_ip->area_wt = obj_func_area;
  //  g_ip->cycle_time_wt    = obj_func_cycle_time;
  //  g_ip->delay_dev = dev_func_delay;
  //  g_ip->dynamic_power_dev = dev_func_dynamic_power;
  //  g_ip->leakage_power_dev = dev_func_leakage_power;
  //  g_ip->area_dev = dev_func_area;
  //  g_ip->cycle_time_dev    = dev_func_cycle_time;
  //  g_ip->temp = temp;
  //
  //  g_ip->F_sz_nm         = tech_node;
  //  g_ip->F_sz_um         = tech_node / 1000;
  //  g_ip->is_main_mem     = (main_mem != 0) ? true : false;
  //  g_ip->is_cache        = (cache ==1) ? true : false;
  //  g_ip->pure_ram        = (cache ==0) ? true : false;
  //  g_ip->pure_cam        = (cache ==2) ? true : false;
  //  g_ip->rpters_in_htree = (REPEATERS_IN_HTREE_SEGMENTS_in != 0) ? true :
  //  false; g_ip->ver_htree_wires_over_array =
  //  VERTICAL_HTREE_WIRES_OVER_THE_ARRAY_in;
  //  g_ip->broadcast_addr_din_over_ver_htrees =
  //  BROADCAST_ADDR_DATAIN_OVER_VERTICAL_HTREES_in;
  //
  //  g_ip->num_rw_ports    = rw_ports;
  //  g_ip->num_rd_ports    = excl_read_ports;
  //  g_ip->num_wr_ports    = excl_write_ports;
  //  g_ip->num_se_rd_ports = single_ended_read_ports;
  //  g_ip->num_search_ports = search_ports;
  //
  //  g_ip->print_detail = 1;
  //    g_ip->nuca = 0;
  //    g_ip->is_cache=true;
  //
  //  if (force_wiretype == 0)
  //  {
  //	  g_ip->wt = Global;
  //      g_ip->force_wiretype = false;
  //  }
  //  else
  //  {   g_ip->force_wiretype = true;
  //	  if (wiretype==10) {
  //		  g_ip->wt = Global_10;
  //	        }
  //	  if (wiretype==20) {
  //		  g_ip->wt = Global_20;
  //	        }
  //	  if (wiretype==30) {
  //		  g_ip->wt = Global_30;
  //	        }
  //	  if (wiretype==5) {
  //	      g_ip->wt = Global_5;
  //	        }
  //	  if (wiretype==0) {
  //		  g_ip->wt = Low_swing;
  //	  }
  //  }
  //  //g_ip->wt = Global_5;
  //  if (force_config == 0)
  //    {
  //  	  g_ip->force_cache_config = false;
  //    }
  //    else
  //    {
  //    	g_ip->force_cache_config = true;
  //    	g_ip->ndbl=ndbl;
  //    	g_ip->ndwl=ndwl;
  //    	g_ip->nspd=nspd;
  //    	g_ip->ndcm=ndcm;
  //    	g_ip->ndsam1=ndsam1;
  //    	g_ip->ndsam2=ndsam2;
  //
  //
  //    }
  //
  //  if (ecc==0){
  //	  g_ip->add_ecc_b_=false;
  //  }
  //  else
  //  {
  //	  g_ip->add_ecc_b_=true;
  //  }

  if (!g_ip->error_checking())
    exit(0);

  init_tech_params(g_ip->F_sz_um, false);
  Wire winit;  // Do not delete this line. It initializes wires.

  solve(&fin_res);

  if (!g_ip->dvs_voltage.empty()) {
    update_dvs(&fin_res);
  }
  if (g_ip->power_gating) {
    update_pg(&fin_res);  // this is needed for compute area overhead of
                          // power-gating, even the gated power is calculated
                          // together un-gated leakage
  }

  //  g_ip->display_ip();
  //  output_UCA(&fin_res);
  //  output_data_csv(fin_res);
  //  Wire wprint;//reset wires to original configuration as in *.cfg file (dvs
  //  level 0) Wire::print_wire();
  // delete (g_ip);

  return fin_res;
}

// McPAT's plain interface, please keep !!!
uca_org_t init_interface(InputParameter *const local_interface) {
  // g_ip = new InputParameter();
  // g_ip->add_ecc_b_ = true;

  uca_org_t fin_res;
  fin_res.valid = false;

  g_ip = local_interface;

  //  g_ip->data_arr_ram_cell_tech_type    = data_arr_ram_cell_tech_flavor_in;
  //  g_ip->data_arr_peri_global_tech_type =
  //  data_arr_peri_global_tech_flavor_in; g_ip->tag_arr_ram_cell_tech_type
  //  = tag_arr_ram_cell_tech_flavor_in; g_ip->tag_arr_peri_global_tech_type  =
  //  tag_arr_peri_global_tech_flavor_in;
  //
  //  g_ip->ic_proj_type     = interconnect_projection_type_in;
  //  g_ip->wire_is_mat_type = wire_inside_mat_type_in;
  //  g_ip->wire_os_mat_type = wire_outside_mat_type_in;
  //  g_ip->burst_len        = BURST_LENGTH_in;
  //  g_ip->int_prefetch_w   = INTERNAL_PREFETCH_WIDTH_in;
  //  g_ip->page_sz_bits     = PAGE_SIZE_BITS_in;
  //
  //  g_ip->cache_sz            = cache_size;
  //  g_ip->line_sz             = line_size;
  //  g_ip->assoc               = associativity;
  //  g_ip->nbanks              = banks;
  //  g_ip->out_w               = output_width;
  //  g_ip->specific_tag        = specific_tag;
  //  if (tag_width == 0) {
  //    g_ip->tag_w = 42;
  //  }
  //  else {
  //    g_ip->tag_w               = tag_width;
  //  }
  //
  //  g_ip->access_mode         = access_mode;
  //  g_ip->delay_wt = obj_func_delay;
  //  g_ip->dynamic_power_wt = obj_func_dynamic_power;
  //  g_ip->leakage_power_wt = obj_func_leakage_power;
  //  g_ip->area_wt = obj_func_area;
  //  g_ip->cycle_time_wt    = obj_func_cycle_time;
  //  g_ip->delay_dev = dev_func_delay;
  //  g_ip->dynamic_power_dev = dev_func_dynamic_power;
  //  g_ip->leakage_power_dev = dev_func_leakage_power;
  //  g_ip->area_dev = dev_func_area;
  //  g_ip->cycle_time_dev    = dev_func_cycle_time;
  //  g_ip->temp = temp;
  //
  //  g_ip->F_sz_nm         = tech_node;
  //  g_ip->F_sz_um         = tech_node / 1000;
  //  g_ip->is_main_mem     = (main_mem != 0) ? true : false;
  //  g_ip->is_cache        = (cache ==1) ? true : false;
  //  g_ip->pure_ram        = (cache ==0) ? true : false;
  //  g_ip->pure_cam        = (cache ==2) ? true : false;
  //  g_ip->rpters_in_htree = (REPEATERS_IN_HTREE_SEGMENTS_in != 0) ? true :
  //  false; g_ip->ver_htree_wires_over_array =
  //  VERTICAL_HTREE_WIRES_OVER_THE_ARRAY_in;
  //  g_ip->broadcast_addr_din_over_ver_htrees =
  //  BROADCAST_ADDR_DATAIN_OVER_VERTICAL_HTREES_in;
  //
  //  g_ip->num_rw_ports    = rw_ports;
  //  g_ip->num_rd_ports    = excl_read_ports;
  //  g_ip->num_wr_ports    = excl_write_ports;
  //  g_ip->num_se_rd_ports = single_ended_read_ports;
  //  g_ip->num_search_ports = search_ports;
  //
  //  g_ip->print_detail = 1;
  //  g_ip->nuca = 0;
  //
  //  if (force_wiretype == 0)
  //  {
  //	  g_ip->wt = Global;
  //      g_ip->force_wiretype = false;
  //  }
  //  else
  //  {   g_ip->force_wiretype = true;
  //	  if (wiretype==10) {
  //		  g_ip->wt = Global_10;
  //	        }
  //	  if (wiretype==20) {
  //		  g_ip->wt = Global_20;
  //	        }
  //	  if (wiretype==30) {
  //		  g_ip->wt = Global_30;
  //	        }
  //	  if (wiretype==5) {
  //	      g_ip->wt = Global_5;
  //	        }
  //	  if (wiretype==0) {
  //		  g_ip->wt = Low_swing;
  //	  }
  //  }
  //  //g_ip->wt = Global_5;
  //  if (force_config == 0)
  //    {
  //  	  g_ip->force_cache_config = false;
  //    }
  //    else
  //    {
  //    	g_ip->force_cache_config = true;
  //    	g_ip->ndbl=ndbl;
  //    	g_ip->ndwl=ndwl;
  //    	g_ip->nspd=nspd;
  //    	g_ip->ndcm=ndcm;
  //    	g_ip->ndsam1=ndsam1;
  //    	g_ip->ndsam2=ndsam2;
  //
  //
  //    }
  //
  //  if (ecc==0){
  //	  g_ip->add_ecc_b_=false;
  //  }
  //  else
  //  {
  //	  g_ip->add_ecc_b_=true;
  //  }

  g_ip->error_checking();

  init_tech_params(g_ip->F_sz_um, false);
  Wire winit;  // Do not delete this line. It initializes wires.
  // solve(&fin_res);
  // g_ip->display_ip();

  // solve(&fin_res);
  // output_UCA(&fin_res);
  // output_data_csv(fin_res);
  // delete (g_ip);

  return fin_res;
}

void reconfigure(InputParameter *local_interface, uca_org_t *fin_res) {
  // Copy the InputParameter to global interface (g_ip) and do error checking.
  g_ip = local_interface;
  g_ip->error_checking();

  // Initialize technology parameters
  init_tech_params(g_ip->F_sz_um, false);

  Wire winit;  // Do not delete this line. It initializes wires.

  // This corresponds to solve() in the initialization process.
  update_dvs(fin_res);
}

}  // namespace cacti
